Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f3ee9f7 authored by masiullah's avatar masiullah Committed by Steve Kondik
Browse files

Stylus gestures features (1/2)

Forward-port of http://review.cyanogenmod.com/#/c/18610/
to JB

Change-Id: Ie0ab71d3a2283078601db8405aaabe1d81b27665
parent 1629d815
Loading
Loading
Loading
Loading
+48 −0
Original line number Original line Diff line number Diff line
@@ -2691,6 +2691,54 @@ public final class Settings {
         * @hide
         * @hide
         */
         */
        public static final String LOCKSCREEN_CALENDAR_REMINDERS_ONLY = "lockscreen_calendar_reminders_only";
        public static final String LOCKSCREEN_CALENDAR_REMINDERS_ONLY = "lockscreen_calendar_reminders_only";
        /**
         * Enable Stylus Gestures
         *
         * @hide
         */
        public static final String ENABLE_STYLUS_GESTURES = "enable_stylus_gestures";

        /**
         * Left Swipe Action
         *
         * @hide
         */
        public static final String GESTURES_LEFT_SWIPE = "gestures_left_swipe";

        /**
         * Right Swipe Action
         *
         * @hide
         */
        public static final String GESTURES_RIGHT_SWIPE = "gestures_right_swipe";

        /**
         * Up Swipe Action
         *
         * @hide
         */
        public static final String GESTURES_UP_SWIPE = "gestures_up_swipe";

        /**
         * down Swipe Action
         *
         * @hide
         */
        public static final String GESTURES_DOWN_SWIPE = "gestures_down_swipe";

        /**
         * Long press Action
         *
         * @hide
         */
        public static final String GESTURES_LONG_PRESS = "gestures_long_press";

        /**
         * double tap Action
         *
         * @hide
         */
        public static final String GESTURES_DOUBLE_TAP = "gestures_double_tap";


        /**
        /**
         * Always show the battery status on the lockscreen
         * Always show the battery status on the lockscreen
+2 −0
Original line number Original line Diff line number Diff line
@@ -971,4 +971,6 @@
    <!-- True if the Sym key should open the InputMethodPicker (default) -->
    <!-- True if the Sym key should open the InputMethodPicker (default) -->
    <bool name="config_symKeyShowsImePicker">true</bool>
    <bool name="config_symKeyShowsImePicker">true</bool>


    <!-- Boolean to enable Stylus gestures -->
    <bool name="config_stylusGestures">false</bool>
</resources>
</resources>
+2 −0
Original line number Original line Diff line number Diff line
@@ -3939,4 +3939,6 @@
  <!-- IME Picker showing upon pressing Sym Key -->
  <!-- IME Picker showing upon pressing Sym Key -->
  <java-symbol type="bool" name="config_symKeyShowsImePicker" />
  <java-symbol type="bool" name="config_symKeyShowsImePicker" />


  <!-- Stylus gestures -->
  <java-symbol type="bool" name="config_stylusGestures" />
</resources>
</resources>
+284 −1
Original line number Original line Diff line number Diff line
@@ -40,11 +40,19 @@ import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarView;
import com.android.internal.widget.ActionBarView;


import android.app.KeyguardManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Rect;
@@ -53,10 +61,14 @@ import android.media.AudioManager;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.EventLog;
@@ -65,6 +77,8 @@ import android.util.SparseArray;
import android.util.TypedValue;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ActionMode;
import android.view.ContextThemeWrapper;
import android.view.ContextThemeWrapper;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.Gravity;
import android.view.IRotationWatcher;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.IWindowManager;
@@ -92,10 +106,13 @@ import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.TextView;
import android.widget.Toast;


import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ArrayList;


import com.android.internal.statusbar.IStatusBarService;

/**
/**
 * Android-specific Window.
 * Android-specific Window.
 * <p>
 * <p>
@@ -181,6 +198,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {


    private boolean mAlwaysReadCloseOnTouchAttr = false;
    private boolean mAlwaysReadCloseOnTouchAttr = false;


    private boolean mEnableGestures;

    private Context mContext;
    private ContextMenuBuilder mContextMenu;
    private ContextMenuBuilder mContextMenu;
    private MenuDialogHelper mContextMenuHelper;
    private MenuDialogHelper mContextMenuHelper;
    private boolean mClosingActionMenu;
    private boolean mClosingActionMenu;
@@ -190,6 +210,33 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
    private AudioManager mAudioManager;
    private AudioManager mAudioManager;
    private KeyguardManager mKeyguardManager;
    private KeyguardManager mKeyguardManager;


    private Handler mConfigHandler;

    private final class SettingsObserver extends ContentObserver {
        SettingsObserver(Handler handler) {
            super(handler);
        }

        void observe() {
            ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(Settings.System
                    .getUriFor(Settings.System.ENABLE_STYLUS_GESTURES), false,
                    this);
            checkGestures();
        }

        @Override
        public void onChange(boolean selfChange) {
            checkGestures();
        }

        void checkGestures() {
            mEnableGestures = Settings.System.getInt(
                    mContext.getContentResolver(),
                    Settings.System.ENABLE_STYLUS_GESTURES, 0) == 1;
        }
    }

    private int mUiOptions = 0;
    private int mUiOptions = 0;


    static class WindowManagerHolder {
    static class WindowManagerHolder {
@@ -201,7 +248,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {


    public PhoneWindow(Context context) {
    public PhoneWindow(Context context) {
        super(context);
        super(context);
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
        mLayoutInflater = LayoutInflater.from(context);
        mConfigHandler = new Handler();
        SettingsObserver settingsObserver = new SettingsObserver(mConfigHandler);
        settingsObserver.observe();
    }
    }


    @Override
    @Override
@@ -1867,8 +1918,240 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            return false;
            return false;
        }
        }


        private final StylusGestureFilter mStylusFilter = new StylusGestureFilter();

        private class StylusGestureFilter extends SimpleOnGestureListener {

            private final static int SWIPE_UP = 1;
            private final static int SWIPE_DOWN = 2;
            private final static int SWIPE_LEFT = 3;
            private final static int SWIPE_RIGHT = 4;
            private final static int PRESS_LONG = 5;
            private final static int TAP_DOUBLE = 6;
            private final static double SWIPE_MIN_DISTANCE = 25.0;
            private final static double SWIPE_MIN_VELOCITY = 50.0;
            private final static int KEY_NO_ACTION = 1000;
            private final static int KEY_HOME = 1001;
            private final static int KEY_BACK = 1002;
            private final static int KEY_MENU = 1003;
            private final static int KEY_SEARCH = 1004;
            private final static int KEY_RECENT = 1005;
            private final static int KEY_APP = 1006;
            private GestureDetector mDetector;
            private final static String TAG = "StylusGestureFilter";

            public StylusGestureFilter() {
                mDetector = new GestureDetector(this);
            }

            public boolean onTouchEvent(MotionEvent event) {
                return mDetector.onTouchEvent(event);
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2,
                    float velocityX, float velocityY) {

                final float xDistance = Math.abs(e1.getX() - e2.getX());
                final float yDistance = Math.abs(e1.getY() - e2.getY());

                velocityX = Math.abs(velocityX);
                velocityY = Math.abs(velocityY);
                boolean result = false;

                if (velocityX > (SWIPE_MIN_VELOCITY * getResources().getDisplayMetrics().density)
                        && xDistance > (SWIPE_MIN_DISTANCE * getResources().getDisplayMetrics().density)
                        && xDistance > yDistance) {
                    if (e1.getX() > e2.getX()) { // right to left
                        // Swipe Left
                        dispatchStylusAction(SWIPE_LEFT);
                    } else {
                        // Swipe Right
                        dispatchStylusAction(SWIPE_RIGHT);
                    }
                    result = true;
                } else if (velocityY > (SWIPE_MIN_VELOCITY * getResources().getDisplayMetrics().density)
                        && yDistance > (SWIPE_MIN_DISTANCE * getResources().getDisplayMetrics().density)
                        && yDistance > xDistance) {
                    if (e1.getY() > e2.getY()) { // bottom to up
                        // Swipe Up
                        dispatchStylusAction(SWIPE_UP);
                    } else {
                        // Swipe Down
                        dispatchStylusAction(SWIPE_DOWN);
                    }
                    result = true;
                }
                return result;
            }

            @Override
            public boolean onDoubleTap(MotionEvent arg0) {
                dispatchStylusAction(TAP_DOUBLE);
                return true;
            }

            public void onLongPress(MotionEvent e) {
                dispatchStylusAction(PRESS_LONG);
            }

        }

        private void menuAction() {
            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
                    KeyEvent.KEYCODE_MENU));
            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
                    KeyEvent.KEYCODE_MENU));

        }

        private void backAction() {
            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
                    KeyEvent.KEYCODE_BACK));
            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
                    KeyEvent.KEYCODE_BACK));
        }

        private void dispatchStylusAction(int gestureAction) {
            final ContentResolver resolver = mContext.getContentResolver();
            boolean flag = false;
            //flag=true when action is performed on systemui
            if ("com.android.systemui".equalsIgnoreCase(mContext
                    .getPackageName())) {
                flag = true;
            }
            String packageName = null;
            int dispatchAction = -1;
            switch (gestureAction) {
            case StylusGestureFilter.SWIPE_LEFT:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_LEFT_SWIPE);
                break;
            case StylusGestureFilter.SWIPE_RIGHT:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_RIGHT_SWIPE);
                break;
            case StylusGestureFilter.SWIPE_UP:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_UP_SWIPE);
                break;
            case StylusGestureFilter.SWIPE_DOWN:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_DOWN_SWIPE);
                break;
            case StylusGestureFilter.TAP_DOUBLE:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_DOUBLE_TAP);
                break;
            case StylusGestureFilter.PRESS_LONG:
                packageName = Settings.System.getString(resolver,
                        Settings.System.GESTURES_LONG_PRESS);
                break;
            }
            if (packageName != null) {
                if (String.valueOf(StylusGestureFilter.KEY_HOME)
                        .equalsIgnoreCase(packageName)) {
                    dispatchAction = StylusGestureFilter.KEY_HOME;
                } else if (String.valueOf(StylusGestureFilter.KEY_BACK)
                        .equalsIgnoreCase(packageName)) {
                    dispatchAction = StylusGestureFilter.KEY_BACK;
                } else if (String.valueOf(StylusGestureFilter.KEY_MENU)
                        .equalsIgnoreCase(packageName)) {
                    dispatchAction = StylusGestureFilter.KEY_MENU;
                } else if (String.valueOf(StylusGestureFilter.KEY_SEARCH)
                        .equalsIgnoreCase(packageName)) {
                    dispatchAction = StylusGestureFilter.KEY_SEARCH;
                } else if (String.valueOf(StylusGestureFilter.KEY_RECENT)
                        .equalsIgnoreCase(packageName)) {
                    dispatchAction = StylusGestureFilter.KEY_RECENT;
                } else if (String.valueOf(StylusGestureFilter.KEY_NO_ACTION)
                        .equalsIgnoreCase(packageName)) {
                    return;
                } else {
                    dispatchAction = StylusGestureFilter.KEY_APP;
                }
            } else {
                return;
            }
            // Dispatching action
            switch (dispatchAction) {
            case StylusGestureFilter.KEY_HOME:
                Intent homeIntent = new Intent(Intent.ACTION_MAIN);
                homeIntent.addCategory(Intent.CATEGORY_HOME);
                homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.startActivity(homeIntent);
                break;
            case StylusGestureFilter.KEY_BACK:
                backAction();
                break;
            case StylusGestureFilter.KEY_MENU:
                // Menu action on notificationbar / systemui will be converted
                // to back action
                if (flag) {
                    backAction();
                    break;
                }
                menuAction();
                break;
            case StylusGestureFilter.KEY_SEARCH:
                // Search action on notificationbar / systemui will be converted
                // to back action
                if (flag) {
                    backAction();
                    break;
                }
                launchDefaultSearch();
                break;
            case StylusGestureFilter.KEY_RECENT:
                IStatusBarService mStatusBarService = IStatusBarService.Stub
                        .asInterface(ServiceManager.getService("statusbar"));
                try {
                    mStatusBarService.toggleRecentApps();
                } catch (RemoteException e) {
                }
                break;
            case StylusGestureFilter.KEY_APP:
                // Launching app on notificationbar / systemui will be preceded
                // with a back Action
                if (flag) {
                    backAction();
                }
                String name = getAppName(packageName);
                if (name == null) {
                    Toast.makeText(mContext, packageName + " is not installed",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                Intent LaunchIntent = mContext.getPackageManager()
                        .getLaunchIntentForPackage(packageName);
                mContext.startActivity(LaunchIntent);
                break;
            }

        }

        private String getAppName(String packageName) {
            final PackageManager pm = mContext.getPackageManager();
            ApplicationInfo ai;
            try {
                ai = pm.getApplicationInfo(packageName, 0);
            } catch (final NameNotFoundException e) {
                ai = null;
            }
            String applicationName = (String) (ai != null ? pm
                    .getApplicationLabel(ai) : null);
            return applicationName;
        }

        @Override
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
        public boolean dispatchTouchEvent(MotionEvent ev) {
            // Stylus events with side button pressed are filtered and other
            // events are processed normally.
            if (mEnableGestures
                    && MotionEvent.BUTTON_SECONDARY == ev.getButtonState()) {
                mStylusFilter.onTouchEvent(ev);
                return false;
            }
            final Callback cb = getCallback();
            final Callback cb = getCallback();
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                    : super.dispatchTouchEvent(ev);
                    : super.dispatchTouchEvent(ev);