zenfone6: Add offscreen gestures letters
diff --git a/DeviceParts/res/values/strings.xml b/DeviceParts/res/values/strings.xml
index db6dc9c..490047d 100644
--- a/DeviceParts/res/values/strings.xml
+++ b/DeviceParts/res/values/strings.xml
@@ -31,12 +31,12 @@
 
     <string name="choose_app">Choose app</string>
     <string name="offscreen_gesture_title">Offscreen gestures</string>
-    <string name="category_c_gesture">C gesture</string>
-    <string name="category_e_gesture">E gesture</string>
-    <string name="category_s_gesture">S gesture</string>
-    <string name="category_v_gesture">V gesture</string>
-    <string name="category_w_gesture">W gesture</string>
-    <string name="category_z_gesture">Z gesture</string>
+    <string name="category_c_gesture">Letter c</string>
+    <string name="category_e_gesture">Letter e</string>
+    <string name="category_s_gesture">Letter s</string>
+    <string name="category_v_gesture">Letter v</string>
+    <string name="category_w_gesture">Letter w</string>
+    <string name="category_z_gesture">Letter z</string>
     <string name="category_left_arrow_gesture">&lt; gesture</string>
     <string name="category_right_arrow_gesture">&gt; gesture</string>
     <string name="category_gestures_summary">Configure gesture actions</string>
diff --git a/DeviceParts/res/xml/gesture_settings.xml b/DeviceParts/res/xml/gesture_settings.xml
index 597fa77..ace04c7 100644
--- a/DeviceParts/res/xml/gesture_settings.xml
+++ b/DeviceParts/res/xml/gesture_settings.xml
@@ -41,4 +41,39 @@
             android:persistent="false"/>
     </PreferenceCategory>
 
+    <PreferenceCategory
+        android:title="@string/offscreen_gesture_title">
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="c_gesture_app"
+            android:title="@string/category_c_gesture"
+            android:persistent="false" />
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="e_gesture_app"
+            android:title="@string/category_e_gesture"
+            android:persistent="false" />
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="s_gesture_app"
+            android:title="@string/category_s_gesture"
+            android:persistent="false" />
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="v_gesture_app"
+            android:title="@string/category_v_gesture"
+            android:persistent="false"/>
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="w_gesture_app"
+            android:title="@string/category_w_gesture"
+            android:persistent="false"/>
+
+        <org.omnirom.device.AppSelectListPreference
+            android:key="z_gesture_app"
+            android:title="@string/category_z_gesture"
+            android:persistent="false"/>
+
+     </PreferenceCategory>
+
 </PreferenceScreen>
diff --git a/DeviceParts/src/org/omnirom/device/GestureSettings.java b/DeviceParts/src/org/omnirom/device/GestureSettings.java
index 00c0c17..7977a99 100644
--- a/DeviceParts/src/org/omnirom/device/GestureSettings.java
+++ b/DeviceParts/src/org/omnirom/device/GestureSettings.java
@@ -56,15 +56,42 @@
     public static final String KEY_PROXI_SWITCH = "proxi";
     public static final String KEY_OFF_SCREEN_GESTURE_FEEDBACK_SWITCH = "off_screen_gesture_feedback";
 
+    public static final int KEY_C_ID = 0;
+    public static final int KEY_E_ID = 1;
+    public static final int KEY_S_ID = 2;
+    public static final int KEY_V_ID = 3;
+    public static final int KEY_W_ID = 4;
+    public static final int KEY_Z_ID = 5;
+    public static final String KEY_C_APP = "c_gesture_app";
+    public static final String KEY_E_APP = "e_gesture_app";
+    public static final String KEY_S_APP = "s_gesture_app";
+    public static final String KEY_V_APP = "v_gesture_app";
+    public static final String KEY_W_APP = "w_gesture_app";
+    public static final String KEY_Z_APP = "z_gesture_app";
+
     public static final String KEY_FP_GESTURE_CATEGORY = "key_fp_gesture_category";
     public static final String KEY_FP_GESTURE_DEFAULT_CATEGORY = "gesture_settings";
 
     public static final String FP_GESTURE_LONG_PRESS_APP = "fp_long_press_gesture_app";
 
     public static final String DEVICE_GESTURE_MAPPING_0 = "device_gesture_mapping_0_0";
+    public static final String DEVICE_GESTURE_MAPPING_1 = "device_gesture_mapping_1_0";
+    public static final String DEVICE_GESTURE_MAPPING_2 = "device_gesture_mapping_2_0";
+    public static final String DEVICE_GESTURE_MAPPING_3 = "device_gesture_mapping_3_0";
+    public static final String DEVICE_GESTURE_MAPPING_4 = "device_gesture_mapping_4_0";
+    public static final String DEVICE_GESTURE_MAPPING_5 = "device_gesture_mapping_5_0";
+    public static final String DEVICE_GESTURE_MAPPING_6 = "device_gesture_mapping_6_0";
 
     private TwoStatePreference mProxiSwitch;
     private AppSelectListPreference mFPLongPressApp;
+    private AppSelectListPreference mLetterCGesture;
+    private AppSelectListPreference mLetterEGesture;
+    private AppSelectListPreference mLetterSGesture;
+    private AppSelectListPreference mLetterVGesture;
+    private AppSelectListPreference mLetterWGesture;
+    private AppSelectListPreference mLetterZGesture;
+
+    public static final String GESTURE_CONTROL_PATH = "/sys/devices/platform/soc/c80000.i2c/i2c-4/4-0038/fts_gesture_mode";
 
     private PreferenceCategory fpGestures;
     private boolean mFpDownSwipe;
@@ -86,6 +113,42 @@
         mFPLongPressApp.setValue(value);
         mFPLongPressApp.setOnPreferenceChangeListener(this);
 
+        mLetterCGesture = (AppSelectListPreference) findPreference(KEY_C_APP);
+        mLetterCGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_1);
+        mLetterCGesture.setValue(value);
+        mLetterCGesture.setOnPreferenceChangeListener(this);
+
+        mLetterEGesture = (AppSelectListPreference) findPreference(KEY_E_APP);
+        mLetterEGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_2);
+        mLetterEGesture.setValue(value);
+        mLetterEGesture.setOnPreferenceChangeListener(this);
+
+        mLetterSGesture = (AppSelectListPreference) findPreference(KEY_S_APP);
+        mLetterSGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_3);
+        mLetterSGesture.setValue(value);
+        mLetterSGesture.setOnPreferenceChangeListener(this);
+
+        mLetterVGesture = (AppSelectListPreference) findPreference(KEY_V_APP);
+        mLetterVGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_4);
+        mLetterVGesture.setValue(value);
+        mLetterVGesture.setOnPreferenceChangeListener(this);
+
+        mLetterWGesture = (AppSelectListPreference) findPreference(KEY_W_APP);
+        mLetterWGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_5);
+        mLetterWGesture.setValue(value);
+        mLetterWGesture.setOnPreferenceChangeListener(this);
+
+        mLetterZGesture = (AppSelectListPreference) findPreference(KEY_Z_APP);
+        mLetterZGesture.setEnabled(true);
+        value = Settings.System.getString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_6);
+        mLetterZGesture.setValue(value);
+        mLetterZGesture.setOnPreferenceChangeListener(this);
+
         new FetchPackageInformationTask().execute();
     }
 
@@ -109,14 +172,80 @@
         if (preference == mFPLongPressApp) {
             String value = (String) newValue;
             Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_0, value);
+        } else if (preference == mLetterCGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_C_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_1, value);
+        } else if (preference == mLetterEGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_E_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_2, value);
+        } else if (preference == mLetterSGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_S_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_3, value);
+        } else if (preference == mLetterVGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_V_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_4, value);
+        } else if (preference == mLetterWGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_W_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_5, value);
+        } else if (preference == mLetterZGesture) {
+            String value = (String) newValue;
+            boolean gestureDisabled = value.equals(AppSelectListPreference.DISABLED_ENTRY);
+            setGestureEnabled(KEY_Z_ID, !gestureDisabled);
+            Settings.System.putString(getContext().getContentResolver(), DEVICE_GESTURE_MAPPING_6, value);
         }
         return true;
     }
 
     public static String getGestureFile(String key) {
+        switch(key) {
+            case GESTURE_CONTROL_PATH:
+                return "/sys/devices/platform/soc/c80000.i2c/i2c-4/4-0038/fts_gesture_mode";
+        }
         return null;
     }
 
+    private static final int KEY_MASK_GESTURE_CONTROL = 0x40;
+    private static final int[] ALL_GESTURE_MASKS = {
+        0x04, // c gesture mask
+        0x08, // e gesture mask
+        0x10, // s gesture mask
+        0x01, // v gesture mask
+        0x20, // w gesture mask
+        0x02, // z gesture mask
+    };
+
+    private void setGestureEnabled(int id, boolean enabled) {
+        Log.i("GestureSettings", "setGestureEnabled called with key=" +id+ ",enabled=" +enabled);
+        int gestureMode = Integer.parseInt(Utils.readLine(GESTURE_CONTROL_PATH));
+        int mask = ALL_GESTURE_MASKS[id];
+
+        if (enabled)
+            gestureMode |= mask;
+        else
+            gestureMode &= ~mask;
+
+        if (gestureMode != 0)
+            gestureMode |= KEY_MASK_GESTURE_CONTROL;
+
+        String gestureType = String.format("%7s", Integer.toBinaryString(gestureMode)).replace(' ', '0');
+        Log.i("GestureSettings", "gestureType=" +gestureType);
+
+        String gestureTypeMapping = Settings.System.getString(getContext().getContentResolver(), Settings.System.OMNI_BUTTON_EXTRA_KEY_MAPPING);
+        Settings.System.putString(getContext().getContentResolver(), Settings.System.OMNI_BUTTON_EXTRA_KEY_MAPPING, gestureType);
+
+        Utils.writeLine(GESTURE_CONTROL_PATH, gestureType);
+    }
+
     @Override
     public void onDisplayPreferenceDialog(Preference preference) {
         if (!(preference instanceof AppSelectListPreference)) {
@@ -165,6 +294,12 @@
         @Override
         protected void onPostExecute(Void feed) {
             mFPLongPressApp.setPackageList(mInstalledPackages);
+            mLetterCGesture.setPackageList(mInstalledPackages);
+            mLetterEGesture.setPackageList(mInstalledPackages);
+            mLetterSGesture.setPackageList(mInstalledPackages);
+            mLetterVGesture.setPackageList(mInstalledPackages);
+            mLetterWGesture.setPackageList(mInstalledPackages);
+            mLetterZGesture.setPackageList(mInstalledPackages);
         }
     }
 }
diff --git a/DeviceParts/src/org/omnirom/device/KeyHandler.java b/DeviceParts/src/org/omnirom/device/KeyHandler.java
index 55927cd..b8a623a 100644
--- a/DeviceParts/src/org/omnirom/device/KeyHandler.java
+++ b/DeviceParts/src/org/omnirom/device/KeyHandler.java
@@ -81,6 +81,13 @@
     private static final int KEY_BACK = 158;
     private static final int KEY_RECENTS = 580;
 
+    private static final int KEY_GESTURE_C = 46;
+    private static final int KEY_GESTURE_E = 18;
+    private static final int KEY_GESTURE_S = 31;
+    private static final int KEY_GESTURE_V = 47;
+    private static final int KEY_GESTURE_W = 17;
+    private static final int KEY_GESTURE_Z = 44;
+
     private static final int MIN_PULSE_INTERVAL_MS = 2500;
     private static final String DOZE_INTENT = "com.android.systemui.doze.pulse";
     private static final int HANDWAVE_MAX_DELTA_MS = 1000;
@@ -308,8 +315,10 @@
         if (mFPcheck) {
             String value = getGestureValueForFPScanCode(event.getScanCode());
             return !TextUtils.isEmpty(value) && value.equals(AppSelectListPreference.CAMERA_ENTRY);
+        } else {
+            String value = getGestureValueForScanCode(event.getScanCode());
+            return !TextUtils.isEmpty(value) && value.equals(AppSelectListPreference.CAMERA_ENTRY);
         }
-        return false;
     }
 
     @Override
@@ -317,6 +326,11 @@
         if (event.getAction() != KeyEvent.ACTION_UP) {
             return false;
         }
+         String value = getGestureValueForScanCode(event.getScanCode());
+        if (!TextUtils.isEmpty(value) && value.equals(AppSelectListPreference.WAKE_ENTRY)) {
+            if (DEBUG) Log.i(TAG, "isWakeEvent " + event.getScanCode() + value);
+            return true;
+        }
         return event.getScanCode() == KEY_DOUBLE_TAP;
     }
 
@@ -325,6 +339,15 @@
         if (event.getAction() != KeyEvent.ACTION_UP) {
             return null;
         }
+        String value = getGestureValueForScanCode(event.getScanCode());
+        if (!TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY)) {
+            if (DEBUG) Log.i(TAG, "isActivityLaunchEvent " + event.getScanCode() + value);
+            if (!launchSpecialActions(value)) {
+                OmniVibe.performHapticFeedbackLw(HapticFeedbackConstants.LONG_PRESS, false, mContext);
+                Intent intent = createIntent(value);
+                return intent;
+            }
+        }
         return null;
     }
 
@@ -472,6 +495,31 @@
         }
         return false;
     }
+
+    private String getGestureValueForScanCode(int scanCode) {
+        switch(scanCode) {
+            case KEY_GESTURE_C:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_1, UserHandle.USER_CURRENT);
+            case KEY_GESTURE_E:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_2, UserHandle.USER_CURRENT);
+            case KEY_GESTURE_S:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_3, UserHandle.USER_CURRENT);
+            case KEY_GESTURE_V:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_4, UserHandle.USER_CURRENT);
+            case KEY_GESTURE_W:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_5, UserHandle.USER_CURRENT);
+            case KEY_GESTURE_Z:
+                return Settings.System.getStringForUser(mContext.getContentResolver(),
+                    GestureSettings.DEVICE_GESTURE_MAPPING_6, UserHandle.USER_CURRENT);
+        }
+        return null;
+    }
+
     private String getGestureValueForFPScanCode(int scanCode) {
         if (FP_GESTURE_LONG_PRESS == scanCode) {
             return Settings.System.getStringForUser(mContext.getContentResolver(),
diff --git a/DeviceParts/src/org/omnirom/device/Startup.java b/DeviceParts/src/org/omnirom/device/Startup.java
index a34c92d..fc66c03 100644
--- a/DeviceParts/src/org/omnirom/device/Startup.java
+++ b/DeviceParts/src/org/omnirom/device/Startup.java
@@ -67,6 +67,33 @@
         boolean enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
         restore(getGestureFile(GestureSettings.FP_GESTURE_LONG_PRESS_APP), enabled);
 
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_1);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_C_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_2);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_E_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_3);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_S_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_4);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_V_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_5);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_W_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), GestureSettings.DEVICE_GESTURE_MAPPING_6);
+        enabled = !TextUtils.isEmpty(value) && !value.equals(AppSelectListPreference.DISABLED_ENTRY);
+        restore(getGestureFile(GestureSettings.KEY_Z_APP), enabled);
+
+        value = Settings.System.getString(context.getContentResolver(), Settings.System.OMNI_BUTTON_EXTRA_KEY_MAPPING);
+        restore(getGestureFile(GestureSettings.GESTURE_CONTROL_PATH), value);
+
         enabled = Settings.System.getInt(context.getContentResolver(), GloveModeSwitch.SETTINGS_KEY, 0) != 0;
         restore(GloveModeSwitch.getFile(), enabled);
     }
diff --git a/DeviceParts/src/org/omnirom/device/Utils.java b/DeviceParts/src/org/omnirom/device/Utils.java
index 96ead59..f8d2772 100644
--- a/DeviceParts/src/org/omnirom/device/Utils.java
+++ b/DeviceParts/src/org/omnirom/device/Utils.java
@@ -17,12 +17,16 @@
 */
 package org.omnirom.device;
 
+import android.util.Log;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.FileReader;
+import java.io.FileWriter;
 
 public class Utils {
 
@@ -109,4 +113,34 @@
         }
         return defValue;
     }
+
+    /**
+     * Writes the given value into the given file
+     *
+     * @return true on success, false on failure
+     */
+    public static boolean writeLine(String fileName, String value) {
+        BufferedWriter writer = null;
+
+        try {
+            writer = new BufferedWriter(new FileWriter(fileName));
+            writer.write(value);
+        } catch (FileNotFoundException e) {
+            Log.w("Utils", "No such file " + fileName + " for writing", e);
+            return false;
+        } catch (IOException e) {
+            Log.e("Utils", "Could not write to file " + fileName, e);
+            return false;
+        } finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            } catch (IOException e) {
+                // Ignored, not much we can do anyway
+            }
+        }
+
+        return true;
+    }
 }