Skip to content
This repository was archived by the owner on Feb 9, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions hover/src/main/java/io/mattcarroll/hover/BaseTouchController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.mattcarroll.hover;

import android.graphics.PointF;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public abstract class BaseTouchController {
private static final String TAG = "BaseTouchController";

protected View mTouchView;
protected TouchListener mTouchListener;
protected boolean mIsActivated;
private boolean mIsDebugMode;

private View.OnTouchListener mDragTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "ACTION_DOWN");
mTouchListener.onPress();
return true;
case MotionEvent.ACTION_UP:
Log.d(TAG, "ACTION_UP");
mTouchListener.onTap();
return true;
default:
return false;
}
}
};

public abstract View createTouchView(@NonNull Rect rect);

public abstract void destroyTouchView(@NonNull View touchView);

public abstract void moveTouchViewTo(@NonNull View touchView, @NonNull PointF position);

public void activate(@NonNull TouchListener touchListener, @NonNull Rect rect) {
if (!mIsActivated) {
Log.d(TAG, "Activating.");
mIsActivated = true;
mTouchListener = touchListener;
mTouchView = createTouchView(rect);
moveTouchViewTo(mTouchView, new PointF(rect.left, rect.top));
mTouchView.setOnTouchListener(mDragTouchListener);

updateTouchControlViewAppearance();
}
}

public void deactivate() {
if (mIsActivated) {
Log.d(TAG, "Deactivating.");
mTouchView.setOnTouchListener(null);
destroyTouchView(mTouchView);
mIsActivated = false;
mTouchView = null;
}
}

public void enableDebugMode(boolean isDebugMode) {
mIsDebugMode = isDebugMode;
updateTouchControlViewAppearance();
}

private void updateTouchControlViewAppearance() {
if (null != mTouchView) {
if (mIsDebugMode) {
mTouchView.setBackgroundColor(0x44FF0000);
} else {
mTouchView.setBackgroundColor(0x00000000);
}
}
}

public interface TouchListener {
void onPress();

void onTap();
}
}
127 changes: 97 additions & 30 deletions hover/src/main/java/io/mattcarroll/hover/Dragger.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,67 +15,134 @@
*/
package io.mattcarroll.hover;

import android.graphics.PointF;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
* Reports user drag behavior on the screen to a {@link DragListener}.
*/
public interface Dragger {
public abstract class Dragger extends BaseTouchController {
private static final String TAG = "Dragger";

/**
* Starts reporting user drag behavior given a drag area represented by {@code controlBounds}.
* @param dragListener listener that receives information about drag behavior
* @param rect Rect area to be draggable
*/
void activate(@NonNull DragListener dragListener, @NonNull Rect rect);
private final int mTapTouchSlop;
private DragListener mDragListener;
private boolean mIsDragging;

/**
* Stops monitoring and reporting user drag behavior.
*/
void deactivate();
private PointF mOriginalViewPosition = new PointF();
private PointF mCurrentViewPosition = new PointF();
private PointF mOriginalTouchPosition = new PointF();

/**
* Enable/Disable debug mode. In debug mode this Dragger will paint its touch area with a
* translucent color.
* @param debugMode true for debug mode, false otherwise
*/
void enableDebugMode(boolean debugMode);
protected final View.OnTouchListener mDragTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "ACTION_DOWN");
mIsDragging = false;

interface DragListener {
mOriginalViewPosition = convertCornerToCenter(getTouchViewPosition(mTouchView));
mCurrentViewPosition = new PointF(mOriginalViewPosition.x, mOriginalViewPosition.y);
mOriginalTouchPosition.set(motionEvent.getRawX(), motionEvent.getRawY());
mTouchListener.onPress();
return true;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "ACTION_MOVE. motionX: " + motionEvent.getRawX() + ", motionY: " + motionEvent.getRawY());
float dragDeltaX = motionEvent.getRawX() - mOriginalTouchPosition.x;
float dragDeltaY = motionEvent.getRawY() - mOriginalTouchPosition.y;
mCurrentViewPosition = new PointF(
mOriginalViewPosition.x + dragDeltaX,
mOriginalViewPosition.y + dragDeltaY
);

/**
* The user has pressed within the draggable area at the given position.
* @param x x-coordinate of the user's press (in the parent View's coordinate space)
* @param y y-coordiante of the user's press (in the parent View's coordinate space)
*/
void onPress(float x, float y);
if (mIsDragging || !isTouchWithinSlopOfOriginalTouch(dragDeltaX, dragDeltaY)) {
if (!mIsDragging) {
// Dragging just started
Log.d(TAG, "MOVE Start Drag.");
mIsDragging = true;
mDragListener.onDragStart(mCurrentViewPosition.x, mCurrentViewPosition.y);
} else {
PointF cornerPosition = convertCenterToCorner(mCurrentViewPosition);
moveTouchViewTo(mTouchView, cornerPosition);
mDragListener.onDragTo(mCurrentViewPosition.x, mCurrentViewPosition.y);
}
}

return true;
case MotionEvent.ACTION_UP:
Log.d(TAG, "ACTION_UP");
if (!mIsDragging) {
Log.d(TAG, "Reporting as a tap.");
mTouchListener.onTap();
} else {
Log.d(TAG, "Reporting as a drag release at: " + mCurrentViewPosition);
mDragListener.onReleasedAt(mCurrentViewPosition.x, mCurrentViewPosition.y);
}
return true;
default:
return false;
}
}
};

public Dragger(int mTapTouchSlop) {
this.mTapTouchSlop = mTapTouchSlop;
}

public abstract PointF getTouchViewPosition(@NonNull View touchView);

public void activate(@NonNull DragListener dragListener, @NonNull Rect rect) {
super.activate(dragListener, rect);
mDragListener = dragListener;
mTouchView.setOnTouchListener(mDragTouchListener);
}

private boolean isTouchWithinSlopOfOriginalTouch(float dx, float dy) {
double distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
Log.d(TAG, "Drag distance " + distance + " vs slop allowance " + mTapTouchSlop);
return distance < mTapTouchSlop;
}

private PointF convertCornerToCenter(@NonNull PointF cornerPosition) {
return new PointF(
cornerPosition.x + (mTouchView.getWidth() / 2f),
cornerPosition.y + (mTouchView.getHeight() / 2f)
);
}

private PointF convertCenterToCorner(@NonNull PointF centerPosition) {
return new PointF(
centerPosition.x - (mTouchView.getWidth() / 2f),
centerPosition.y - (mTouchView.getHeight() / 2f)
);
}

public interface DragListener extends TouchListener {
/**
* The user has begun dragging.
*
* @param x x-coordinate of the user's drag start (in the parent View's coordinate space)
* @param y y-coordiante of the user's drag start (in the parent View's coordinate space)
*/
void onDragStart(float x, float y);

/**
* The user has dragged to the given coordinates.
*
* @param x x-coordinate of the user's drag (in the parent View's coordinate space)
* @param y y-coordiante of the user's drag (in the parent View's coordinate space)
*/
void onDragTo(float x, float y);

/**
* The user has stopped touching the drag area.
*
* @param x x-coordinate of the user's release (in the parent View's coordinate space)
* @param y y-coordiante of the user's release (in the parent View's coordinate space)
*/
void onReleasedAt(float x, float y);

/**
* The user tapped the drag area (instead of dragging it).
*/
void onTap();

}
}
21 changes: 10 additions & 11 deletions hover/src/main/java/io/mattcarroll/hover/HoverView.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,9 @@ void restoreVisualState() {
persistentState.restore(this, mMenu);
}

// TODO: when to call this?
public void release() {
Log.d(TAG, "Released.");
mDragger.deactivate();
// TODO: should we also release the screen?
}

public void enableDebugMode(boolean debugMode) {
Expand Down Expand Up @@ -354,21 +352,21 @@ public void removeOnInteractionListener(@NonNull OnInteractionListener onInterac
mOnInteractionListeners.remove(onInteractionListener);
}

void notifyOnTap() {
void notifyOnTap(HoverViewState state) {
for (OnInteractionListener onInteractionListener : mOnInteractionListeners) {
onInteractionListener.onTap();
onInteractionListener.onTap(state.getStateType());
}
}

void notifyOnDragStart() {
void notifyOnDragStart(HoverViewState state) {
for (OnInteractionListener onInteractionListener : mOnInteractionListeners) {
onInteractionListener.onDragStart();
onInteractionListener.onDragStart(state.getStateType());
}
}

void notifyOnDocked() {
void notifyOnDocked(HoverViewState state) {
for (OnInteractionListener onInteractionListener : mOnInteractionListeners) {
onInteractionListener.onDocked();
onInteractionListener.onDocked(state.getStateType());
}
}

Expand Down Expand Up @@ -397,6 +395,7 @@ public void removeFromWindow() {
if (mIsAddedToWindow) {
mWindowViewController.removeView(this);
mIsAddedToWindow = false;
release();
}
}

Expand Down Expand Up @@ -609,10 +608,10 @@ public void onAnchored() {
}

public interface OnInteractionListener {
void onTap();
void onTap(HoverViewStateType stateType);

void onDragStart();
void onDragStart(HoverViewStateType stateType);

void onDocked();
void onDocked(HoverViewStateType stateType);
}
}
2 changes: 2 additions & 0 deletions hover/src/main/java/io/mattcarroll/hover/HoverViewState.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@ interface HoverViewState {
* {@link #respondsToBackButton()} returns true.
*/
void onBackPressed();

HoverViewStateType getStateType();
}
Loading