/* * Copyright (C) 2014 AChep@xda <artemchep@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.achep.acdisplay.ui.view; import android.content.Context; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import com.achep.base.utils.ViewUtils; /** * Created by achep on 26.04.14. */ public class ForwardingLayout extends LinearLayout { private static final String TAG = "ForwardingLayout"; private View mPressedChild; private OnForwardedEventListener mOnForwardedEventListener; private boolean mVibrateOnForwarded; private boolean mForwardAll; private int mForwardDepth; public ForwardingLayout(Context context) { super(context); } public ForwardingLayout(Context context, AttributeSet attrs) { super(context, attrs); } public ForwardingLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public interface OnForwardedEventListener { void onForwardedEvent(MotionEvent event, int activePointerId); void onPressedView(MotionEvent event, int activePointerId, View view); } public void setOnForwardedEventListener(OnForwardedEventListener listener) { mOnForwardedEventListener = listener; } /** * If enabled, forwarding motion event will be accompanied by * haptic feedback on press / un-press / click. */ public void setVibrateOnForwardedEventEnabled(boolean enabled) { mVibrateOnForwarded = enabled; } public void setAllViewsForwardable(boolean enabled, int depth) { mForwardAll = enabled; mForwardDepth = depth; } /** * Handles forwarded events. * * @param activePointerId id of the pointer that activated forwarding * @return whether the event was handled */ public boolean onForwardedEvent(MotionEvent event, int activePointerId) { boolean handledEvent = true; View vibrateView = null; final int actionMasked = event.getActionMasked(); switch (actionMasked) { case MotionEvent.ACTION_CANCEL: handledEvent = false; break; case MotionEvent.ACTION_UP: handledEvent = false; // $FALL-THROUGH$ case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: final int activeIndex = event.findPointerIndex(activePointerId); if (activeIndex < 0) { handledEvent = false; break; } final int x = (int) event.getX(activeIndex); final int y = (int) event.getY(activeIndex); View pressedView = findViewByCoordinate(this, x, y, 0); if (mPressedChild != pressedView) { vibrateView = pressedView != null ? pressedView : mPressedChild; if (pressedView != null) pressedView.setPressed(true); if (mPressedChild != null) mPressedChild.setPressed(false); mPressedChild = pressedView; if (mOnForwardedEventListener != null) { mOnForwardedEventListener.onPressedView(event, activePointerId, mPressedChild); } } if (actionMasked == MotionEvent.ACTION_UP) { vibrateView = mPressedChild; clickPressedItem(); } break; } // Failure to handle the event cancels forwarding. if (!handledEvent) { clearPressedItem(); } if (vibrateView != null && mVibrateOnForwarded) { vibrateView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } if (mOnForwardedEventListener != null) { mOnForwardedEventListener.onForwardedEvent(event, activePointerId); } return handledEvent; } private View findViewByCoordinate(ViewGroup viewGroup, float x, float y, int depth) { final int childCount = viewGroup.getChildCount(); for (int i = childCount - 1; i >= 0; i--) { final View child = viewGroup.getChildAt(i); assert child != null; if (child.getVisibility() != View.VISIBLE || !child.isEnabled()) { continue; } if (child instanceof ViewGroup && (depth < mForwardDepth || !mForwardAll)) { View view = findViewByCoordinate((ViewGroup) child, x - child.getLeft(), y - child.getTop(), depth + 1); if (view != null) { return view; } } if ((child.isClickable() || mForwardAll) && ViewUtils.pointInView(child, x, y, 0)) { return child; } } return null; } private void clearPressedItem() { if (mPressedChild == null) { return; } mPressedChild.setPressed(false); mPressedChild.refreshDrawableState(); mPressedChild = null; } private void clickPressedItem() { if (mPressedChild == null) { return; } mPressedChild.performClick(); clearPressedItem(); } }