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

Commit 66e8606f authored by Steve Kondik's avatar Steve Kondik Committed by Steve Kondik
Browse files

Stylus gestures features (1/2)

  Forward-port to CM-12.0

Change-Id: I091a028c36a54f5d61e8d86bd6ec0856bbee4fee
parent 9580858f
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -3485,6 +3485,55 @@ public final class Settings {
        public static final Validator POINTER_SPEED_VALIDATOR =
                new InclusiveFloatRangeValidator(-7, 7);

        /**
         * 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";

        /**
         * Whether lock-to-app will be triggered by long-press on recents.
         * @hide
+260 −0
Original line number Diff line number Diff line
@@ -76,12 +76,19 @@ import com.android.internal.widget.SwipeDismissLayout;

import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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.Resources.Theme;
import android.content.res.TypedArray;
import android.content.ActivityNotFoundException;
import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
@@ -93,10 +100,14 @@ import android.media.session.MediaSessionLegacyHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
@@ -108,6 +119,8 @@ import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
@@ -118,10 +131,14 @@ import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

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

import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.R;

/**
 * Android-specific Window.
 * <p>
@@ -249,7 +266,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
    private int mTitleColor = 0;

    private boolean mAlwaysReadCloseOnTouchAttr = false;
    private boolean mEnableGestures;

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

    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();
        }

        void unobserve() {
            ContentResolver resolver = mContext.getContentResolver();
            resolver.unregisterContentObserver(this);
        }

        @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 boolean mInvalidatePanelMenuPosted;
@@ -303,6 +352,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {

    public PhoneWindow(Context context) {
        super(context);
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
    }

@@ -2224,6 +2274,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        private View mStatusGuard;
        // View added at runtime to draw under the navigation bar area
        private View mNavigationGuard;
        private SettingsObserver mSettingsObserver;

        private final ColorViewState mStatusColorViewState = new ColorViewState(
                SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
@@ -2267,6 +2318,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {

            mBarEnterExitDuration = context.getResources().getInteger(
                    R.integer.dock_enter_exit_duration);

            mSettingsObserver = new SettingsObserver(new Handler());
        }

        public void setBackgroundFallback(int resId) {
@@ -2358,8 +2411,211 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
            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 isSystemUI = mContext.getPackageName().equals("com.android.systemui");
            String setting = null;
            int dispatchAction = -1;
            switch (gestureAction) {
                case StylusGestureFilter.SWIPE_LEFT:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_LEFT_SWIPE);
                    break;
                case StylusGestureFilter.SWIPE_RIGHT:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_RIGHT_SWIPE);
                    break;
                case StylusGestureFilter.SWIPE_UP:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_UP_SWIPE);
                    break;
                case StylusGestureFilter.SWIPE_DOWN:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_DOWN_SWIPE);
                    break;
                case StylusGestureFilter.TAP_DOUBLE:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_DOUBLE_TAP);
                    break;
                case StylusGestureFilter.PRESS_LONG:
                    setting = Settings.System.getString(resolver,
                            Settings.System.GESTURES_LONG_PRESS);
                    break;
                default:
                    return;
            }

            try {
                int value = Integer.valueOf(setting);
                if (value == StylusGestureFilter.KEY_NO_ACTION) {
                    return;
                }
                dispatchAction = value;
            } catch (NumberFormatException e) {
                dispatchAction = StylusGestureFilter.KEY_APP;
            }

            // 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 (isSystemUI) {
                        backAction();
                        break;
                    }
                    menuAction();
                    break;
                case StylusGestureFilter.KEY_SEARCH:
                    // Search action on notificationbar / systemui will be converted
                    // to back action
                    if (isSystemUI) {
                        backAction();
                        break;
                    }
                    launchDefaultSearch(new KeyEvent(KeyEvent.ACTION_UP,
                                                    KeyEvent.KEYCODE_SEARCH));
                    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 (isSystemUI) {
                        backAction();
                    }
                    try {
                        final PackageManager pm = mContext.getPackageManager();
                        Intent launchIntent = pm.getLaunchIntentForPackage(setting);
                        if (launchIntent != null) {
                            mContext.startActivity(launchIntent);
                        }
                    } catch (ActivityNotFoundException e) {
                        Toast.makeText(mContext, mContext.getString(R.string.stylus_app_not_installed, setting),
                            Toast.LENGTH_LONG).show();
                    }
                    break;
            }
        }

        @Override
        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();
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                    : super.dispatchTouchEvent(ev);
@@ -3255,6 +3511,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();

            mSettingsObserver.observe();

            updateWindowResizeState();

            final Callback cb = getCallback();
@@ -3278,6 +3536,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();

            mSettingsObserver.unobserve();

            final Callback cb = getCallback();
            if (cb != null && mFeatureId < 0) {
                cb.onDetachedFromWindow();
+3 −0
Original line number Diff line number Diff line
@@ -118,4 +118,7 @@
    <string name="theme_install_error_title">Failed to install theme</string>
    <string name="theme_install_error_message">%s failed to install</string>

    <!-- stylus gestures support -->
    <string name="stylus_app_not_installed">%s is not installed</string>

</resources>
+5 −0
Original line number Diff line number Diff line
@@ -59,4 +59,9 @@
    <!-- Theme install failure notification -->
    <java-symbol type="string" name="theme_install_error_title" />
    <java-symbol type="string" name="theme_install_error_message" />

    <!-- Stylus gestures -->
    <java-symbol type="bool" name="config_stylusGestures" />
    <java-symbol type="string" name="stylus_app_not_installed" />

</resources>
+3 −0
Original line number Diff line number Diff line
@@ -2438,4 +2438,7 @@
         applied. -->
    <bool name="config_hasRemovableLid">false</bool>

    <!-- Boolean to enable Stylus gestures -->
    <bool name="config_stylusGestures">false</bool>

</resources>