content: /*
   * Copyright (C) 2018 The Android Open Source Project
   *
   * Licensed 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 com.android.launcher3.touch;
  
  import android.graphics.PointF;
  import android.graphics.Rect;
  import android.view.GestureDetector;
  import android.view.HapticFeedbackConstants;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.View.OnTouchListener;
  import android.view.ViewConfiguration;
  
  import com.android.launcher3.AbstractFloatingView;
  import com.android.launcher3.CellLayout;
  import com.android.launcher3.DeviceProfile;
  import com.android.launcher3.Launcher;
  import com.android.launcher3.LauncherState;
  import com.android.launcher3.Workspace;
  import com.android.launcher3.dragndrop.DragLayer;
  import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
  import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
  import com.android.launcher3.views.OptionsPopupView;
  
  import amirz.helpers.Settings;
  
  import static android.view.MotionEvent.ACTION_CANCEL;
  import static android.view.MotionEvent.ACTION_DOWN;
  import static android.view.MotionEvent.ACTION_MOVE;
  import static android.view.MotionEvent.ACTION_POINTER_UP;
  import static android.view.MotionEvent.ACTION_UP;
  import static com.android.launcher3.LauncherState.NORMAL;
  
  /**
   * Helper class to handle touch on empty space in workspace and show options popup on long press
   */
  public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListener
          implements OnTouchListener {
  
      /**
       * STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
       * This next event is used to send an ACTION_CANCEL to Workspace, to that it clears any
       * temporary scroll state. After that, the state is set to COMPLETED, and we just eat up all
       * subsequent motion events.
       */
      private static final int STATE_CANCELLED = 0;
      private static final int STATE_REQUESTED = 1;
      private static final int STATE_PENDING_PARENT_INFORM = 2;
      private static final int STATE_COMPLETED = 3;
  
      private final Rect mTempRect = new Rect();
      private final Launcher mLauncher;
      private final Workspace mWorkspace;
      private final PointF mTouchDownPoint = new PointF();
      private final float mTouchSlop;
  
      private int mLongPressState = STATE_CANCELLED;
  
      private final GestureDetector mGestureDetector;
  
      public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
          mLauncher = launcher;
          mWorkspace = workspace;
          // Use twice the touch slop as we are looking for long press which is more
          // likely to cause movement.
          mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop();
          mGestureDetector = new GestureDetector(workspace.getContext(), this);
          mGestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
              @Override
              public boolean onSingleTapConfirmed(MotionEvent e) {
                  return false;
              }
  
              @Override
              public boolean onDoubleTap(MotionEvent e) {
                  if (!mLauncher.isInState(LauncherState.NORMAL)) {
                      return false;
                  }
                  Settings.handleWorkspaceTouchEvent(mLauncher);
                  return false;
              }
  
              @Override
              public boolean onDoubleTapEvent(MotionEvent e) {
                  return false;
              }
          });
      }
  
      @Override
      public boolean onTouch(View view, MotionEvent ev) {
          mGestureDetector.onTouchEvent(ev);
  
          int action = ev.getActionMasked();
          if (action == ACTION_DOWN) {
              // Check if we can handle long press.
              boolean handleLongPress = canHandleLongPress();
  
              if (handleLongPress) {
                  // Check if the event is not near the edges
                  DeviceProfile dp = mLauncher.getDeviceProfile();
                  DragLayer dl = mLauncher.getDragLayer();
                  Rect insets = dp.getInsets();
  
                  mTempRect.set(insets.left, insets.top, dl.getWidth() - insets.right,
                          dl.getHeight() - insets.bottom);
                  mTempRect.inset(dp.edgeMarginPx, dp.edgeMarginPx);
                  handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
              }
  
              if (handleLongPress) {
                  mLongPressState = STATE_REQUESTED;
                  mTouchDownPoint.set(ev.getX(), ev.getY());
              }
  
              mWorkspace.onTouchEvent(ev);
              // Return true to keep receiving touch events
              return true;
          }
  
          if (mLongPressState == STATE_PENDING_PARENT_INFORM) {
              // Inform the workspace to cancel touch handling
              ev.setAction(ACTION_CANCEL);
              mWorkspace.onTouchEvent(ev);
  
              ev.setAction(action);
              mLongPressState = STATE_COMPLETED;
          }
  
          final boolean result;
          if (mLongPressState == STATE_COMPLETED) {
              // We have handled the touch, so workspace does not need to know anything anymore.
              result = true;
          } else if (mLongPressState == STATE_REQUESTED) {
              mWorkspace.onTouchEvent(ev);
              if (mWorkspace.isHandlingTouch()) {
                  cancelLongPress();
              } else if (action == ACTION_MOVE && PointF.length(
                      mTouchDownPoint.x - ev.getX(), mTouchDownPoint.y - ev.getY()) > mTouchSlop) {
                  cancelLongPress();
              }
  
              result = true;
          } else {
              // We don't want to handle touch, let workspace handle it as usual.
              result = false;
          }
  
          if (action == ACTION_UP || action == ACTION_POINTER_UP) {
              if (!mWorkspace.isHandlingTouch()) {
                  final CellLayout currentPage =
                          (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
                  if (currentPage != null) {
                      mWorkspace.onWallpaperTap(ev);
                  }
              }
          }
  
          if (action == ACTION_UP || action == ACTION_CANCEL) {
              cancelLongPress();
          }
  
          return result;
      }
  
      private boolean canHandleLongPress() {
          return AbstractFloatingView.getTopOpenView(mLauncher) == null
                  && mLauncher.isInState(NORMAL);
      }
  
      private void cancelLongPress() {
          mLongPressState = STATE_CANCELLED;
      }
  
      @Override
      public void onLongPress(MotionEvent event) {
          if (mLongPressState == STATE_REQUESTED) {
              if (canHandleLongPress()) {
                  mLongPressState = STATE_PENDING_PARENT_INFORM;
                  mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
  
                  mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                          HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                  mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
                          Action.Direction.NONE, ContainerType.WORKSPACE,
                          mWorkspace.getCurrentPage());
                  OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
              } else {
                  cancelLongPress();
              }
          }
      }
  }
  