OSDN Git Service

Stylus support: creating and setting listeners for stylus button press
authorMady Mellor <madym@google.com>
Tue, 2 Jun 2015 22:35:07 +0000 (15:35 -0700)
committerMady Mellor <madym@google.com>
Tue, 2 Jun 2015 22:35:07 +0000 (15:35 -0700)
This updates almost(*) all locations that use a long press listener to
also set a custom touch listener that recognizes the stylus button press
action.

The stylus button press action is: when a stylus touches a view while the
primary stylus button is pressed which may occur on a DOWN or MOVE event.

*The location this is *not* enabled for is: Longpress to enter "overview"
mode -- this isn't really a selection or drag n drop action; it is also
easy to accidentally do this while using the stylus gesture to drag n drop
items which is not an ideal interaction. Also not set for the "cling" that
demonstrates this.

Bug: 20430722
Change-Id: I9343f143261a7b4fada9afca28b8a11a60dbecca

WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
src/com/android/launcher3/BubbleTextView.java
src/com/android/launcher3/CellLayout.java
src/com/android/launcher3/FolderIcon.java
src/com/android/launcher3/StylusEventHelper.java [new file with mode: 0644]
src/com/android/launcher3/widget/WidgetCell.java

index 3f90203..9623871 100644 (file)
@@ -49,6 +49,7 @@ import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
@@ -891,6 +892,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
 
     private void addLongPressHandler(View v) {
         v.setOnLongClickListener(mLongClickListener);
+
+        // Enable stylus button to also trigger long click.
+        final StylusEventHelper stylusEventHelper = new StylusEventHelper(v);
+        v.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                return stylusEventHelper.checkAndPerformStylusEvent(event);
+            }
+        });
     }
 
     private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
index edf5021..798eec8 100644 (file)
@@ -63,6 +63,7 @@ public class BubbleTextView extends TextView {
     private final Drawable mBackground;
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
+    private final StylusEventHelper mStylusEventHelper;
 
     private boolean mBackgroundSizeChanged;
 
@@ -125,6 +126,7 @@ public class BubbleTextView extends TextView {
         }
 
         mLongPressHelper = new CheckLongPressHelper(this);
+        mStylusEventHelper = new StylusEventHelper(this);
 
         mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
         if (mCustomShadowsEnabled) {
@@ -236,6 +238,12 @@ public class BubbleTextView extends TextView {
         // isPressed() on an ACTION_UP
         boolean result = super.onTouchEvent(event);
 
+        // Check for a stylus button press, if it occurs cancel any long press checks.
+        if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
+            mLongPressHelper.cancelLongPress();
+            result = true;
+        }
+
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 // So that the pressed outline is visible immediately on setStayPressed(),
@@ -245,7 +253,10 @@ public class BubbleTextView extends TextView {
                     mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
                 }
 
-                mLongPressHelper.postCheckForLongPress();
+                // If we're in a stylus button press, don't check for long press.
+                if (!mStylusEventHelper.inStylusButtonPressed()) {
+                    mLongPressHelper.postCheckForLongPress();
+                }
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
index 61567ac..a57c1fe 100644 (file)
@@ -95,6 +95,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
     boolean[][] mTmpOccupied;
 
     private OnTouchListener mInterceptTouchListener;
+    private StylusEventHelper mStylusEventHelper;
 
     private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
     private int[] mFolderLeaveBehindCell = {-1, -1};
@@ -286,6 +287,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
 
+        mStylusEventHelper = new StylusEventHelper(this);
+
         mTouchFeedbackView = new ClickShadowView(context);
         addView(mTouchFeedbackView);
         addView(mShortcutsAndWidgets);
@@ -338,6 +341,20 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
         return false;
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = super.onTouchEvent(ev);
+        // Stylus button press on a home screen should not switch between overview mode and
+        // the home screen mode, however, once in overview mode stylus button press should be
+        // enabled to allow rearranging the different home screens. So check what mode
+        // the workspace is in, and only perform stylus button presses while in overview mode.
+        if (mLauncher.mWorkspace.isInOverviewMode()
+                && mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+            return true;
+        }
+        return handled;
+    }
+
     public void enableHardwareLayer(boolean hasLayer) {
         mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
     }
index 8652eef..a81e651 100644 (file)
@@ -57,6 +57,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
     @Thunk static boolean sStaticValuesDirty = true;
 
     private CheckLongPressHelper mLongPressHelper;
+    private StylusEventHelper mStylusEventHelper;
 
     // The number of icons to display in the
     public static final int NUM_ITEMS_IN_PREVIEW = 3;
@@ -128,6 +129,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
 
     private void init() {
         mLongPressHelper = new CheckLongPressHelper(this);
+        mStylusEventHelper = new StylusEventHelper(this);
         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
@@ -719,6 +721,12 @@ public class FolderIcon extends FrameLayout implements FolderListener {
         // isPressed() on an ACTION_UP
         boolean result = super.onTouchEvent(event);
 
+        // Check for a stylus button press, if it occurs cancel any long press checks.
+        if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
+            mLongPressHelper.cancelLongPress();
+            return true;
+        }
+
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mLongPressHelper.postCheckForLongPress();
diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java
new file mode 100644 (file)
index 0000000..da46e6a
--- /dev/null
@@ -0,0 +1,84 @@
+package com.android.launcher3;
+
+import com.android.launcher3.Utilities;
+
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
+ * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a
+ * stylus button press this performs the view's {@link View#performLongClick()} method, if the view
+ * is long clickable.
+ */
+public class StylusEventHelper {
+    private boolean mIsButtonPressed;
+    private View mView;
+
+    public StylusEventHelper(View view) {
+        mView = view;
+    }
+
+    /**
+     * Call this in onTouchEvent method of a view to identify a stylus button press and perform a
+     * long click (if the view is long clickable).
+     *
+     * @param event The event to check for a stylus button press.
+     * @return Whether a stylus event occurred and was handled.
+     */
+    public boolean checkAndPerformStylusEvent(MotionEvent event) {
+        final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
+
+        if (!mView.isLongClickable()) {
+            // We don't do anything unless the view is long clickable.
+            return false;
+        }
+
+        final boolean stylusButtonPressed = isStylusButtonPressed(event);
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mIsButtonPressed = false;
+                if (stylusButtonPressed && mView.performLongClick()) {
+                    mIsButtonPressed = true;
+                    return true;
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) {
+                    if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) {
+                        mIsButtonPressed = true;
+                        return true;
+                    } else if (mIsButtonPressed && !stylusButtonPressed) {
+                        mIsButtonPressed = false;
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mIsButtonPressed = false;
+                break;
+        }
+        return false;
+    }
+
+    /**
+     * Whether a stylus button press is occurring.
+     */
+    public boolean inStylusButtonPressed() {
+        return mIsButtonPressed;
+    }
+
+    /**
+     * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
+     * pressed.
+     *
+     * @param event The event to check.
+     * @return Whether a stylus button press occurred.
+     */
+    public static boolean isStylusButtonPressed(MotionEvent event) {
+        return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+                && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
+    }
+}
\ No newline at end of file
index 3ec1645..2714f51 100644 (file)
@@ -23,6 +23,7 @@ import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
 import android.widget.LinearLayout;
@@ -35,6 +36,7 @@ import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -73,6 +75,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
 
     private WidgetPreviewLoader mWidgetPreviewLoader;
     private PreviewLoadRequest mActiveRequest;
+    private StylusEventHelper mStylusEventHelper;
 
     private Launcher mLauncher;
 
@@ -89,6 +92,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
 
         final Resources r = context.getResources();
         mLauncher = (Launcher) context;
+        mStylusEventHelper = new StylusEventHelper(this);
 
         mDimensionsFormatString = r.getString(R.string.widget_dims_format);
         setContainerWidth();
@@ -202,6 +206,15 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
         return Math.min(size[0], info.spanX * cellWidth);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = super.onTouchEvent(ev);
+        if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+            return true;
+        }
+        return handled;
+    }
+
     /**
      * Helper method to get the string info of the tag.
      */