mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 11:18:21 +00:00
Delegate Taskbar touches to nearest view to ensure 48x48dp touch size
In TaskbarView#onTouchEvent(), which is only reached if a Taskbar icon didn't already consuem the event, check each child to see if the event occurs within a 48x48dp bounding box, and delegate the event and subsequent events to it until UP or CANCEL. Bug: 171917176 Change-Id: I7afafe0835828ab9213ec6abfe4e88ad7b9af3c4
This commit is contained in:
@@ -17,10 +17,13 @@ package com.android.launcher3.taskbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
@@ -39,6 +42,10 @@ public class TaskbarView extends LinearLayout {
|
||||
|
||||
private final ColorDrawable mBackgroundDrawable;
|
||||
private final int mItemMarginLeftRight;
|
||||
private final int mIconTouchSize;
|
||||
private final int mTouchSlop;
|
||||
private final RectF mTempDelegateBounds = new RectF();
|
||||
private final RectF mDelegateSlopBounds = new RectF();
|
||||
|
||||
// Initialized in init().
|
||||
private int mHotseatStartIndex;
|
||||
@@ -46,6 +53,10 @@ public class TaskbarView extends LinearLayout {
|
||||
|
||||
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
|
||||
|
||||
// Delegate touches to the closest view if within mIconTouchSize.
|
||||
private boolean mDelegateTargeted;
|
||||
private View mDelegateView;
|
||||
|
||||
public TaskbarView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -66,6 +77,8 @@ public class TaskbarView extends LinearLayout {
|
||||
Resources resources = getResources();
|
||||
mBackgroundDrawable = (ColorDrawable) getBackground();
|
||||
mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
|
||||
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
}
|
||||
|
||||
protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
|
||||
@@ -129,6 +142,74 @@ public class TaskbarView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
boolean handled = delegateTouchIfNecessary(event);
|
||||
return super.onTouchEvent(event) || handled;
|
||||
}
|
||||
|
||||
/**
|
||||
* User touched the Taskbar background. Determine whether the touch is close enough to a view
|
||||
* that we should forward the touches to it.
|
||||
* @return Whether a delegate view was chosen and it handled the touch event.
|
||||
*/
|
||||
private boolean delegateTouchIfNecessary(MotionEvent event) {
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (!child.isShown() || !child.isClickable()) {
|
||||
continue;
|
||||
}
|
||||
int childCenterX = child.getLeft() + child.getWidth() / 2;
|
||||
int childCenterY = child.getTop() + child.getHeight() / 2;
|
||||
mTempDelegateBounds.set(
|
||||
childCenterX - mIconTouchSize / 2f,
|
||||
childCenterY - mIconTouchSize / 2f,
|
||||
childCenterX + mIconTouchSize / 2f,
|
||||
childCenterY + mIconTouchSize / 2f);
|
||||
mDelegateTargeted = mTempDelegateBounds.contains(x, y);
|
||||
if (mDelegateTargeted) {
|
||||
mDelegateView = child;
|
||||
mDelegateSlopBounds.set(mTempDelegateBounds);
|
||||
mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean sendToDelegate = mDelegateTargeted;
|
||||
boolean inBounds = true;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
inBounds = mDelegateSlopBounds.contains(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mDelegateTargeted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
boolean handled = false;
|
||||
if (sendToDelegate) {
|
||||
if (inBounds) {
|
||||
// Offset event coordinates to be inside the target view
|
||||
event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
|
||||
} else {
|
||||
// Offset event coordinates to be outside the target view (in case it does
|
||||
// something like tracking pressed state)
|
||||
event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
|
||||
}
|
||||
handled = mDelegateView.dispatchTouchEvent(event);
|
||||
// Cleanup if this was the last event to send to the delegate.
|
||||
if (!mDelegateTargeted) {
|
||||
mDelegateView = null;
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
private View inflate(@LayoutRes int layoutResId) {
|
||||
return LayoutInflater.from(getContext()).inflate(layoutResId, this, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user