package org.mozilla.vrbrowser.ui.views; import android.graphics.Path; import android.graphics.RectF; import android.graphics.Region; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import org.mozilla.vrbrowser.utils.ViewUtils; import java.util.Deque; public class ClippedEventDelegate implements View.OnHoverListener, View.OnTouchListener { private View mView; private Region mRegion; private boolean mHovered; private boolean mTouched; private OnClickListener mClickListener; public ClippedEventDelegate(@DrawableRes int res, @NonNull View view) { mView = view; mHovered = false; mTouched = false; view.getViewTreeObserver().addOnGlobalLayoutListener( () -> { Path path = createPathFromResource(res); RectF bounds = new RectF(); path.computeBounds(bounds, true); bounds = new RectF(); path.computeBounds(bounds, true); mRegion = new Region(); mRegion.setPath(path, new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom)); }); } public void setOnClickListener(OnClickListener listener) { mClickListener = listener; } private Path createPathFromResource(@DrawableRes int res) { VectorShape shape = new VectorShape(mView.getContext(), res); shape.onResize(mView.getWidth(), mView.getHeight()); Deque<VectorShape.Layer> layers = shape.getLayers(); VectorShape.Layer layer = layers.getFirst(); // TODO Handle state changes and update the Region based on the new current state shape return layer.transformedPath; } @Override public boolean onHover(View v, MotionEvent event) { if(!mRegion.contains((int)event.getX(),(int) event.getY())) { if (mHovered) { mHovered = false; event.setAction(MotionEvent.ACTION_HOVER_EXIT); return v.onHoverEvent(event); } return true; } else { if (!mHovered) { mHovered = true; event.setAction(MotionEvent.ACTION_HOVER_ENTER); } return v.onHoverEvent(event); } } @Override public boolean onTouch(View v, MotionEvent event) { v.getParent().requestDisallowInterceptTouchEvent(true); if(!mRegion.contains((int)event.getX(),(int) event.getY())) { switch (event.getAction()) { case MotionEvent.ACTION_UP: if (mTouched) { v.requestFocus(); v.requestFocusFromTouch(); if (mClickListener != null) { mClickListener.onClick(v); } } v.setPressed(false); mTouched = false; } return true; } else { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: v.setPressed(true); mTouched = true; return true; case MotionEvent.ACTION_UP: if (mTouched && ViewUtils.isInsideView(v, (int)event.getRawX(), (int)event.getRawY())) { v.requestFocus(); v.requestFocusFromTouch(); if (mClickListener != null) { mClickListener.onClick(v); } } v.setPressed(false); mTouched = false; return true; case MotionEvent.ACTION_MOVE: return true; case MotionEvent.ACTION_CANCEL: v.setPressed(false); mTouched = false; return true; } return false; } } }