/* * Copyright (C) 2015 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.dragndrop; import android.os.SystemClock; import android.view.DragEvent; import android.view.MotionEvent; import java.util.function.Consumer; /** * Base class for driving a drag/drop operation. */ public abstract class DragDriver { protected final EventListener mEventListener; protected final Consumer mSecondaryEventConsumer; public interface EventListener { void onDriverDragMove(float x, float y); void onDriverDragExitWindow(); void onDriverDragEnd(float x, float y); void onDriverDragCancel(); } public DragDriver(EventListener eventListener, Consumer sec) { mEventListener = eventListener; mSecondaryEventConsumer = sec; } /** * Called to handle system touch event */ public boolean onTouchEvent(MotionEvent ev) { return false; } /** * Called to handle system touch intercept event */ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } /** * Called to handle system drag event */ public boolean onDragEvent(DragEvent event) { return false; } /** * Created a driver for handing the actual events */ public static DragDriver create(DragController dragController, DragOptions options, Consumer sec) { if (options.simulatedDndStartPoint != null) { if (options.isAccessibleDrag) { return null; } return new SystemDragDriver(dragController, sec); } else { return new InternalDragDriver(dragController, sec); } } /** * Class for driving a system (i.e. framework) drag/drop operation. */ static class SystemDragDriver extends DragDriver { private final long mDragStartTime; float mLastX = 0; float mLastY = 0; SystemDragDriver(DragController dragController, Consumer sec) { super(dragController, sec); mDragStartTime = SystemClock.uptimeMillis(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } /** * It creates a temporary {@link MotionEvent} object for secondary consumer */ private void simulateSecondaryMotionEvent(DragEvent event) { final int motionAction; switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: motionAction = MotionEvent.ACTION_DOWN; break; case DragEvent.ACTION_DRAG_LOCATION: motionAction = MotionEvent.ACTION_MOVE; break; case DragEvent.ACTION_DRAG_ENDED: motionAction = MotionEvent.ACTION_UP; break; default: return; } MotionEvent emulatedEvent = MotionEvent.obtain(mDragStartTime, SystemClock.uptimeMillis(), motionAction, event.getX(), event.getY(), 0); mSecondaryEventConsumer.accept(emulatedEvent); emulatedEvent.recycle(); } @Override public boolean onDragEvent(DragEvent event) { simulateSecondaryMotionEvent(event); final int action = event.getAction(); switch (action) { case DragEvent.ACTION_DRAG_STARTED: mLastX = event.getX(); mLastY = event.getY(); return true; case DragEvent.ACTION_DRAG_ENTERED: return true; case DragEvent.ACTION_DRAG_LOCATION: mLastX = event.getX(); mLastY = event.getY(); mEventListener.onDriverDragMove(event.getX(), event.getY()); return true; case DragEvent.ACTION_DROP: mLastX = event.getX(); mLastY = event.getY(); mEventListener.onDriverDragMove(event.getX(), event.getY()); mEventListener.onDriverDragEnd(mLastX, mLastY); return true; case DragEvent.ACTION_DRAG_EXITED: mEventListener.onDriverDragExitWindow(); return true; case DragEvent.ACTION_DRAG_ENDED: mEventListener.onDriverDragCancel(); return true; default: return false; } } } /** * Class for driving an internal (i.e. not using framework) drag/drop operation. */ static class InternalDragDriver extends DragDriver { private final DragController mDragController; InternalDragDriver(DragController dragController, Consumer sec) { super(dragController, sec); mDragController = dragController; } @Override public boolean onTouchEvent(MotionEvent ev) { mSecondaryEventConsumer.accept(ev); final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_MOVE: mEventListener.onDriverDragMove(mDragController.getX(ev), mDragController.getY(ev)); break; case MotionEvent.ACTION_UP: mEventListener.onDriverDragMove(mDragController.getX(ev), mDragController.getY(ev)); mEventListener.onDriverDragEnd(mDragController.getX(ev), mDragController.getY(ev)); break; case MotionEvent.ACTION_CANCEL: mEventListener.onDriverDragCancel(); break; } return true; } public boolean onInterceptTouchEvent(MotionEvent ev) { mSecondaryEventConsumer.accept(ev); final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_UP: mEventListener.onDriverDragEnd(mDragController.getX(ev), mDragController.getY(ev)); break; case MotionEvent.ACTION_CANCEL: mEventListener.onDriverDragCancel(); break; } return true; } } }