content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.weex.ui.view.gesture;
  
  import android.annotation.SuppressLint;
  import android.content.Context;
  import android.graphics.Point;
  import android.graphics.PointF;
  import android.graphics.Rect;
  import android.os.Looper;
  import android.support.annotation.NonNull;
  import android.view.GestureDetector;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.View.OnTouchListener;
  import android.view.ViewGroup;
  import android.view.ViewParent;
  import com.alibaba.fastjson.JSONArray;
  import com.alibaba.fastjson.JSONObject;
  import org.apache.weex.bridge.EventResult;
  import org.apache.weex.common.Constants;
  import org.apache.weex.dom.WXEvent;
  import org.apache.weex.ui.component.Scrollable;
  import org.apache.weex.ui.component.WXComponent;
  import org.apache.weex.ui.view.gesture.WXGestureType.GestureInfo;
  import org.apache.weex.ui.view.gesture.WXGestureType.HighLevelGesture;
  import org.apache.weex.ui.view.gesture.WXGestureType.LowLevelGesture;
  import org.apache.weex.utils.WXLogUtils;
  import org.apache.weex.utils.WXUtils;
  import org.apache.weex.utils.WXViewUtils;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import org.apache.weex.WXSDKInstance;
  import org.apache.weex.common.Constants.Event;
  
  public class WXGesture extends GestureDetector.SimpleOnGestureListener implements OnTouchListener {
  
    private final static String TAG = "Gesture";
    private static final int CUR_EVENT = -1;
    public static final String START = "start";
    public static final String MOVE = "move";
    public static final String END = "end";
    public static final String UNKNOWN = "unknown";
    public static final String LEFT = "left";
    public static final String RIGHT = "right";
    public static final String UP = "up";
    public static final String DOWN = "down";
    private WXComponent component;
    private GestureDetector mGestureDetector;
    private Rect globalRect;
    private Point globalOffset;
    private Point globalEventOffset;
    private PointF locEventOffset;
    private PointF locLeftTop;
    private long swipeDownTime = -1;
    private long panDownTime = -1;
    private WXGestureType mPendingPan = null;//event type to notify when action_up or action_cancel
    private int mParentOrientation =-1;
    private boolean mIsPreventMoveEvent = false;
    private boolean mIsTouchEventConsumed = false; //Reset to false when first touch event, set to true when gesture event fired.
  
    private boolean requestDisallowInterceptTouchEvent = false;
  
    private boolean shouldBubbleResult = true;
    private int     shouldBubbleInterval = 0; //every times try
    private int     shouldBubbleCallRemainTimes = 0;
  
    private final List<OnTouchListener> mTouchListeners = new LinkedList<>();
  
    public WXGesture(WXComponent wxComponent, Context context) {
      this.component = wxComponent;
      globalRect = new Rect();
      globalOffset = new Point();
      globalEventOffset = new Point();
      locEventOffset = new PointF();
      locLeftTop = new PointF();
      mGestureDetector = new GestureDetector(context, this, new GestureHandler());
      Scrollable parentScrollable = wxComponent.getParentScroller();
      if(parentScrollable != null) {
        mParentOrientation = parentScrollable.getOrientation();
      }
      shouldBubbleResult =  WXUtils.getBoolean(wxComponent.getAttrs().get(Constants.Name.SHOULD_STOP_PROPAGATION_INIT_RESULT), true);
      shouldBubbleInterval = WXUtils.getNumberInt(wxComponent.getAttrs().get(Constants.Name.SHOULD_STOP_PROPAGATION_INTERVAL), 0);
    }
  
    private boolean isParentScrollable() {
      if(component == null) {
        return true;
      }
      Scrollable parentScrollable = component.getParentScroller();
      return parentScrollable == null || parentScrollable.isScrollable();
    }
  
    private boolean hasSameOrientationWithParent(){
      return (mParentOrientation == Constants.Orientation.HORIZONTAL && component.containsGesture(
          HighLevelGesture.HORIZONTALPAN))
              || (mParentOrientation == Constants.Orientation.VERTICAL && component.containsGesture(HighLevelGesture.VERTICALPAN));
    }
  
    public void setPreventMoveEvent(boolean preventMoveEvent) {
      mIsPreventMoveEvent = preventMoveEvent;
    }
  
    /**
     *
     * @return true if current touch event is already consumed by gesture.
     * Reset to false when next first touch event, set to true when gesture event fired.
     */
    public boolean isTouchEventConsumedByAdvancedGesture(){
      return mIsTouchEventConsumed;
    }
  
  
    /**
     * stoppropagation
     * */
    public static boolean isStopPropagation(String type){
      return  Constants.Event.STOP_PROPAGATION.equals(type) || Constants.Event.STOP_PROPAGATION_RAX.equals(type);
    }
  
    public static boolean hasStopPropagation(WXComponent component){
      WXEvent event = component.getEvents();
      if(event == null){
        return false;
      }
      int size = event.size();
      for (int i=0; i<size; i++) {
        if(i >= event.size()){
          break;
        }
        String type = event.get(i);
        if(isStopPropagation(type)){
          return true;
        }
      }
      return false;
    }
  
    /**
     * shouldBubbleEvent default true
     * */
    private boolean shouldBubbleTouchEvent(MotionEvent event){
      if(hasStopPropagation(component)){
        if(shouldBubbleInterval > 0 && shouldBubbleCallRemainTimes > 0){
          shouldBubbleCallRemainTimes--;
          return  shouldBubbleResult;
        }
        Map<String, Object> eventMap = createFireEventParam(event, CUR_EVENT, null);
        eventMap.put("type", "touch");
        if(event.getAction() == MotionEvent.ACTION_DOWN){
          eventMap.put("action", START);
        }else if(event.getAction() == MotionEvent.ACTION_CANCEL
                ||  event.getAction() == MotionEvent.ACTION_UP){
          eventMap.put("action", END);
        }else{
          eventMap.put("action", MOVE);
        }
  
        String name = Event.STOP_PROPAGATION;
        if(!component.getEvents().contains(Event.STOP_PROPAGATION)){
           name = Event.STOP_PROPAGATION_RAX;
        }
        EventResult result = component.fireEventWait(name, eventMap);
        if(result.isSuccess() && result.getResult() != null){
          boolean stopPropagation = WXUtils.getBoolean(result.getResult(), !shouldBubbleResult);
          shouldBubbleResult = !stopPropagation;
        }
        shouldBubbleCallRemainTimes = shouldBubbleInterval;
        return shouldBubbleResult;
  
      }
      return  true;
    }
  
    @SuppressWarnings("unused")
    public void addOnTouchListener(OnTouchListener listener) {
      if(listener != null) {
        mTouchListeners.add(listener);
      }
    }
  
    @SuppressWarnings("unused")
    public boolean removeTouchListener(OnTouchListener listener) {
      if(listener != null) {
        return mTouchListeners.remove(listener);
      }
      return false;
    }
  
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      if(requestDisallowInterceptTouchEvent){
        requestDisallowInterceptTouchEvent = false;
        return false;
      }
      try {
        boolean result = mGestureDetector.onTouchEvent(event);
  
        if(mTouchListeners != null && !mTouchListeners.isEmpty()) {
          for(OnTouchListener listener : mTouchListeners) {
            result |= listener.onTouch(v, event);
          }
        }
  
        switch (event.getActionMasked()) {
          case MotionEvent.ACTION_POINTER_DOWN:
          case MotionEvent.ACTION_DOWN:
            mIsTouchEventConsumed = false;
            /**
             * If component has same scroll orientation with it's parent and it's parent not scrollable
             * , we should disallow parent in DOWN.
             */
            if(hasSameOrientationWithParent() && !isParentScrollable()){
              ViewParent p;
              if ((p = component.getRealView().getParent()) != null) {
                p.requestDisallowInterceptTouchEvent(true);
              }
            }
            result |= handleMotionEvent(LowLevelGesture.ACTION_DOWN, event);
            break;
          case MotionEvent.ACTION_MOVE:
            result |= handleMotionEvent(LowLevelGesture.ACTION_MOVE, event);
            break;
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_POINTER_UP:
            finishDisallowInterceptTouchEvent(v);
            result |= handleMotionEvent(LowLevelGesture.ACTION_UP, event);
            result |= handlePanMotionEvent(event);
            break;
          case MotionEvent.ACTION_CANCEL:
            finishDisallowInterceptTouchEvent(v);
            result |= handleMotionEvent(LowLevelGesture.ACTION_CANCEL, event);
            result |= handlePanMotionEvent(event);
            break;
        }
        if(hasStopPropagation(component)){
          ViewGroup parent = (ViewGroup) v.getParent();
          boolean requestDisallowInterceptTouchEvent = false;
          if(parent != null){
            if(!shouldBubbleTouchEvent(event)){
              requestDisallowInterceptTouchEvent = true;
            }
            parent.requestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent);
          }
          if(component.getParent() != null){
            component.getParent().requestDisallowInterceptTouchEvent(requestDisallowInterceptTouchEvent);
          }
          if(mIsTouchEventConsumed && WXUtils.getBoolean(component.getAttrs().get("cancelTouchOnConsume"), false)){//when touch event consumed by one gesture, other component should not consumed
            event.setAction(MotionEvent.ACTION_CANCEL);
          }
        }
        return result;
      } catch (Exception e) {
        WXLogUtils.e("Gesture RunTime Error ", e);
        return false;
      }
    }
  
    private String getPanEventAction(MotionEvent event) {
      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          return START;
        case MotionEvent.ACTION_MOVE:
          return MOVE;
        case MotionEvent.ACTION_UP:
          return END;
        case MotionEvent.ACTION_CANCEL:
          return END;
        default:
          return UNKNOWN;
      }
    }
  
    private void finishDisallowInterceptTouchEvent(View v){
      if(v.getParent() != null){
        v.getParent().requestDisallowInterceptTouchEvent(false);
      }
    }
  
    private boolean handlePanMotionEvent(MotionEvent motionEvent) {
      if (mPendingPan == null) {
        return false;
      }
      String state = null;
      if (mPendingPan == HighLevelGesture.HORIZONTALPAN || mPendingPan == HighLevelGesture.VERTICALPAN) {
        state = getPanEventAction(motionEvent);
  
      }
  
      if (component.containsGesture(mPendingPan)) {
        if(mIsPreventMoveEvent && MOVE.equals(state)){
          return true;
        }
        List<Map<String, Object>> list = createMultipleFireEventParam(motionEvent, state);
        for (Map<String, Object> map : list) {
          component.fireEvent(mPendingPan.toString(), map);
        }
        //action is finish, clean pending pan
        if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
          mPendingPan = null;
        }
        return true;
      } else {
        return false;
      }
    }
  
    /**
     *
     * @param WXGestureType possible low-level gesture, defined in {@link Event}
     * @param motionEvent motionEvent, which contains all pointers event in a period of time
     * @return true if this event is handled, otherwise false.
     */
    private boolean handleMotionEvent(WXGestureType WXGestureType, MotionEvent motionEvent) {
      if (component.containsGesture(WXGestureType)) {
        List<Map<String, Object>> list = createMultipleFireEventParam(motionEvent, null);
        for (Map<String, Object> map : list) {
          component.fireEvent(WXGestureType.toString(), map);
        }
        return true;
      } else {
        return false;
      }
    }
  
    /**
     * Create a list of event for {@link WXSDKInstance#fireEvent(String, String, Map, Map)}.
     * As there is a batch mechanism in MotionEvent, so this method returns a list.
     * @param motionEvent motionEvent, which contains all pointers event in a period of time
     * @return List of Map, which contains touch object.
     */
    private List<Map<String, Object>> createMultipleFireEventParam(MotionEvent motionEvent,String state) {
      List<Map<String, Object>> list = new ArrayList<>(motionEvent.getHistorySize() + 1);
      //list.addAll(getHistoricalEvents(motionEvent));
      list.add(createFireEventParam(motionEvent, CUR_EVENT, state));
      return list;
    }
  
    /**
     * Get historical event. This is only applied to {@link MotionEvent#ACTION_MOVE}.
     * For other types of motionEvent, historical event is meaningless.
     * @param motionEvent motionEvent, which contains all pointers event in a period of time
     * @return If motionEvent.getActionMasked()!=MotionEvent.ACTION_MOVE,
     * this method will return an empty list.
     * Otherwise this method will return the historical motionEvent, which may also be empty.
     */
    private List<Map<String, Object>> getHistoricalEvents(MotionEvent motionEvent) {
      List<Map<String, Object>> list = new ArrayList<>(motionEvent.getHistorySize());
      if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
        Map<String, Object> param;
        for (int i = 0; i < motionEvent.getHistorySize(); i++) {
          param = createFireEventParam(motionEvent, i,null);
          list.add(param);
        }
      }
      return list;
    }
  
    /**
     * Create a map represented touch event at a certain moment.
     * @param motionEvent motionEvent, which contains all pointers event in a period of time
     * @param pos index used to retrieve a certain moment in a period of time.
     * @return touchEvent
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent">touchEvent</a>
     */
    private Map<String, Object> createFireEventParam(MotionEvent motionEvent, int pos, String state) {
      JSONArray jsonArray = new JSONArray(motionEvent.getPointerCount());
      if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
        for (int i = 0; i < motionEvent.getPointerCount(); i++) {
          jsonArray.add(createJSONObject(motionEvent, pos, i));
        }
      } else if (isPointerNumChanged(motionEvent)) {
        int pointerIndex = motionEvent.getActionIndex();
        jsonArray.add(createJSONObject(motionEvent, CUR_EVENT, pointerIndex));
      }
      Map<String, Object> map = new HashMap<>();
      map.put(GestureInfo.HISTORICAL_XY, jsonArray);
      if (state != null) {
        map.put(GestureInfo.STATE, state);
      }
      return map;
    }
  
    /**
     * Tell whether the number of motion event's pointer changed.
     * @param event the current motion event
     * @return true for number of motion event's pointer changed, otherwise false.
     */
    private boolean isPointerNumChanged(MotionEvent event) {
      return event.getActionMasked() == MotionEvent.ACTION_DOWN ||
              event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN ||
              event.getActionMasked() == MotionEvent.ACTION_UP ||
              event.getActionMasked() == MotionEvent.ACTION_POINTER_UP ||
              event.getActionMasked() == MotionEvent.ACTION_CANCEL;
    }
  
    /**
     * Tell whether component contains pan gesture
     * @return true for contains pan gesture, otherwise false.
     */
    private boolean containsSimplePan() {
      return component.containsGesture(HighLevelGesture.PAN_START) ||
              component.containsGesture(HighLevelGesture.PAN_MOVE) ||
              component.containsGesture(HighLevelGesture.PAN_END);
    }
  
    /**
     * Create a touchObject for a pointer at a certain moment.
     * @param motionEvent motionEvent, which contains all pointers event in a period of time
     * @param pos index used to retrieve a certain moment in a period of time.
     * @param pointerIndex pointerIndex
     * @return JSONObject represent a touch event
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch">touch</a>
     */
    private JSONObject createJSONObject(MotionEvent motionEvent, int pos, int pointerIndex) {
      PointF screenXY, pageXY;
      if (pos == CUR_EVENT) {
        pageXY = getEventLocInPageCoordinate(motionEvent, pointerIndex);
        screenXY = getEventLocInScreenCoordinate(motionEvent, pointerIndex);
      } else {
        pageXY = getEventLocInPageCoordinate(motionEvent, pointerIndex, pos);
        screenXY = getEventLocInScreenCoordinate(motionEvent, pointerIndex, pos);
      }
      JSONObject map = createJSONObject(screenXY, pageXY, (float) motionEvent.getPointerId(pointerIndex));
      float force = motionEvent.getPressure();
      if(force > 0 && force < 1) {
        map.put("force", motionEvent.getPressure());
      }
      return map;
    }
  
    /**
     * Create a touchObject for a pointer at a certain moment.
     * @param screenXY the point of event happened in screen coordinate
     * @param pageXY the point of event happened in page coorindate
     * @param pointerId pointerIndex pointerIndex
     * @return JSONObject represent a touch event
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch">touch</a>
     */
    @NonNull
    private JSONObject createJSONObject(PointF screenXY, PointF pageXY, float pointerId) {
      JSONObject jsonObject = new JSONObject();
      jsonObject.put(GestureInfo.PAGE_X, pageXY.x);
      jsonObject.put(GestureInfo.PAGE_Y, pageXY.y);
      jsonObject.put(GestureInfo.SCREEN_X, screenXY.x);
      jsonObject.put(GestureInfo.SCREEN_Y, screenXY.y);
      jsonObject.put(GestureInfo.POINTER_ID, pointerId);
      return jsonObject;
    }
  
    /**
     * @see {@link #getEventLocInScreenCoordinate(MotionEvent, int, int)}
     */
    private PointF getEventLocInScreenCoordinate(MotionEvent motionEvent, int pointerIndex) {
      return getEventLocInScreenCoordinate(motionEvent, pointerIndex, CUR_EVENT);
    }
  
    /**
     * Get event location in Screen's coordinate, e.g. root(global) coordinate.
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/screenX">screenX</a>
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/screenY">screenY</a>
     * @param motionEvent the original motionEvent
     * @param pointerIndex pointerIndex
     * @param position if motionEvent.getHistoricalSize()!=0, the is the index of historical event,
     *                 otherwise this is {@link #CUR_EVENT} which indicates historicalSize is zero
     * @return the eventLocation in screen's coordinate
     */
    private PointF getEventLocInScreenCoordinate(MotionEvent motionEvent, int pointerIndex, int position) {
      float eventX, eventY;
      if (position == CUR_EVENT) {
        eventX = motionEvent.getX(pointerIndex);
        eventY = motionEvent.getY(pointerIndex);
      } else {
        eventX = motionEvent.getHistoricalX(pointerIndex, position);
        eventY = motionEvent.getHistoricalY(pointerIndex, position);
      }
      return getEventLocInScreenCoordinate(eventX, eventY);
    }
  
    /**
     * Get event location in Screen's coordinate, e.g. root(global) coordinate.
     * @param eventX {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)}
     * @param eventY {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)}
     * @return the eventLocation in screen's coordinate
     * @see {@link #getEventLocInScreenCoordinate(MotionEvent, int, int)}
     */
    @NonNull
    private PointF getEventLocInScreenCoordinate(float eventX, float eventY) {
      globalRect.set(0, 0, 0, 0);
      globalOffset.set(0, 0);
      globalEventOffset.set((int) eventX, (int) eventY);
      component.getRealView().getGlobalVisibleRect(globalRect, globalOffset);
      globalEventOffset.offset(globalOffset.x, globalOffset.y);
      return new PointF(WXViewUtils.getWebPxByWidth(globalEventOffset.x,component.getInstance().getInstanceViewPortWidth()),
              WXViewUtils.getWebPxByWidth(globalEventOffset.y,component.getInstance().getInstanceViewPortWidth()));
    }
  
    /**
     * @see {@link #getEventLocInPageCoordinate(MotionEvent, int, int)}
     */
    private PointF getEventLocInPageCoordinate(MotionEvent motionEvent, int pointerIndex) {
      return getEventLocInPageCoordinate(motionEvent, pointerIndex, CUR_EVENT);
    }
  
    /**
     * Get event's location in Document's (Page) coordinate.
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/pageX">pageX</a>
     * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch/pageY">pageY</a>
     * @param motionEvent the original motionEvent
     * @param pointerIndex pointerIndex
     * @param position if motionEvent.getHistoricalSize()!=0, the is the index of historical event,
     *                 otherwise this is {@link #CUR_EVENT} which indicates historicalSize is zero
     * @return the event location in page's coordinate.
     */
    private PointF getEventLocInPageCoordinate(MotionEvent motionEvent, int pointerIndex, int position) {
      float eventX, eventY;
      if (position == CUR_EVENT) {
        eventX = motionEvent.getX(pointerIndex);
        eventY = motionEvent.getY(pointerIndex);
      } else {
        eventX = motionEvent.getHistoricalX(pointerIndex, position);
        eventY = motionEvent.getHistoricalY(pointerIndex, position);
      }
      return getEventLocInPageCoordinate(eventX, eventY);
    }
  
    /**
     * Get event's location in Document's (Page) coordinate.
     * @param eventX {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)}
     * @param eventY {@link MotionEvent#getX()} or {@link MotionEvent#getHistoricalX(int, int)}
     * @return the event location in page's coordinate.
     * @see {@link #getEventLocInPageCoordinate(MotionEvent, int, int)}
     */
    @NonNull
    private PointF getEventLocInPageCoordinate(float eventX, float eventY) {
      locEventOffset.set(eventX, eventY);
      locLeftTop.set(0, 0);
      component.computeVisiblePointInViewCoordinate(locLeftTop);
      locEventOffset.offset(locLeftTop.x, locLeftTop.y);
      return new PointF(WXViewUtils.getWebPxByWidth(locEventOffset.x,component.getInstance().getInstanceViewPortWidth()),
              WXViewUtils.getWebPxByWidth(locEventOffset.y,component.getInstance().getInstanceViewPortWidth()));
    }
  
    private static class GestureHandler extends android.os.Handler {
  
      public GestureHandler() {
        super(Looper.getMainLooper());
      }
    }
  
  
    /***************** OnGestureListener ****************/
  
    @Override
    public void onLongPress(MotionEvent e) {
      if (component.containsGesture(HighLevelGesture.LONG_PRESS)) {
        List<Map<String, Object>> list = createMultipleFireEventParam(e,null);
        component.getInstance().fireEvent(
                component.getRef(),
                HighLevelGesture.LONG_PRESS.toString(),
                list.get(list.size() - 1));
        mIsTouchEventConsumed = true;
      }
    }
  
    /**
     * Gesture priority：horizontalPan & verticalPan > pan > swipe
     */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      boolean result = false;
      if (e1 == null || e2 == null) {
        return false;
      }
      float dx = Math.abs(e2.getX() - e1.getX());
      float dy = Math.abs(e2.getY() - e1.getY());
      WXGestureType possiblePan;
      if (dx > dy) {
        possiblePan = HighLevelGesture.HORIZONTALPAN;
      } else {
        possiblePan = HighLevelGesture.VERTICALPAN;
      }
      if (mPendingPan == HighLevelGesture.HORIZONTALPAN || mPendingPan == HighLevelGesture.VERTICALPAN) {
        //already during directional-pan
        result = handlePanMotionEvent(e2);
      } else if (component.containsGesture(possiblePan)) {
        ViewParent p;
        if ((p = component.getRealView().getParent()) != null) {
          p.requestDisallowInterceptTouchEvent(true);
        }
        if (mPendingPan != null) {
          handleMotionEvent(mPendingPan, e2);//finish pan if exist
        }
        mPendingPan = possiblePan;
        component.fireEvent(possiblePan.toString(), createFireEventParam(e2, CUR_EVENT, START));
  
        result = true;
      } else if (containsSimplePan()) {
        if (panDownTime != e1.getEventTime()) {
          panDownTime = e1.getEventTime();
          mPendingPan = HighLevelGesture.PAN_END;
          component.fireEvent(HighLevelGesture.PAN_START.toString(),
                  createFireEventParam(e1, CUR_EVENT, null));
        } else {
          component.fireEvent(HighLevelGesture.PAN_MOVE.toString(),
                  createFireEventParam(e2, CUR_EVENT, null));
        }
        result = true;
      } else if (component.containsGesture(HighLevelGesture.SWIPE)) {
        if (swipeDownTime != e1.getEventTime()) {
          swipeDownTime = e1.getEventTime();
          List<Map<String, Object>> list = createMultipleFireEventParam(e2, null);
          Map<String, Object> param = list.get(list.size() - 1);
          if (Math.abs(distanceX) > Math.abs(distanceY)) {
            param.put(GestureInfo.DIRECTION, distanceX > 0 ? LEFT : RIGHT);
          } else {
            param.put(GestureInfo.DIRECTION, distanceY > 0 ? UP : DOWN);
          }
          component.getInstance().fireEvent(component.getRef(),
                  HighLevelGesture.SWIPE.toString(), param);
          result = true;
        }
      }
      mIsTouchEventConsumed = mIsTouchEventConsumed || result;
      return result;
    }
  
    @Override
    public boolean onDown(MotionEvent e) {
      return true;
    }
  
    public boolean isRequestDisallowInterceptTouchEvent() {
      return requestDisallowInterceptTouchEvent;
    }
  
    public void setRequestDisallowInterceptTouchEvent(boolean requestDisallowInterceptTouchEvent) {
      this.requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent;
    }
  
  
  
  }
  