Merge "Cache locations of where nearest child is." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7330bd4e8c
@@ -30,7 +30,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false"
|
||||||
|
systemui:isVertical="false">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/ends_group"
|
android:id="@+id/ends_group"
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false"
|
||||||
|
systemui:isVertical="true">
|
||||||
|
|
||||||
<com.android.systemui.navigationbar.buttons.ReverseLinearLayout
|
<com.android.systemui.navigationbar.buttons.ReverseLinearLayout
|
||||||
android:id="@+id/ends_group"
|
android:id="@+id/ends_group"
|
||||||
|
|||||||
@@ -143,6 +143,8 @@
|
|||||||
<attr name="handleColor" format="color" />
|
<attr name="handleColor" format="color" />
|
||||||
<attr name="scrimColor" format="color" />
|
<attr name="scrimColor" format="color" />
|
||||||
|
|
||||||
|
<attr name="isVertical" format="boolean" />
|
||||||
|
|
||||||
<!-- Used display CarrierText in Keyguard or QS Footer -->
|
<!-- Used display CarrierText in Keyguard or QS Footer -->
|
||||||
<declare-styleable name="CarrierText">
|
<declare-styleable name="CarrierText">
|
||||||
<attr name="allCaps" format="boolean" />
|
<attr name="allCaps" format="boolean" />
|
||||||
|
|||||||
@@ -1124,7 +1124,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|
|||||||
}
|
}
|
||||||
// If an incoming call is ringing, HOME is totally disabled.
|
// If an incoming call is ringing, HOME is totally disabled.
|
||||||
// (The user is already on the InCallUI at this point,
|
// (The user is already on the InCallUI at this point,
|
||||||
// and his ONLY options are to answer or reject the call.)
|
// and their ONLY options are to answer or reject the call.)
|
||||||
switch (event.getAction()) {
|
switch (event.getAction()) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
mHomeBlockedThisTouch = false;
|
mHomeBlockedThisTouch = false;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ import com.android.systemui.navigationbar.buttons.ContextualButton;
|
|||||||
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
|
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
|
||||||
import com.android.systemui.navigationbar.buttons.DeadZone;
|
import com.android.systemui.navigationbar.buttons.DeadZone;
|
||||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||||
|
import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
|
||||||
import com.android.systemui.navigationbar.buttons.RotationContextButton;
|
import com.android.systemui.navigationbar.buttons.RotationContextButton;
|
||||||
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
|
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
|
||||||
import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
|
import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
|
||||||
@@ -97,6 +98,8 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
|||||||
import com.android.wm.shell.pip.Pip;
|
import com.android.wm.shell.pip.Pip;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class NavigationBarView extends FrameLayout implements
|
public class NavigationBarView extends FrameLayout implements
|
||||||
@@ -129,6 +132,7 @@ public class NavigationBarView extends FrameLayout implements
|
|||||||
private final Region mTmpRegion = new Region();
|
private final Region mTmpRegion = new Region();
|
||||||
private final int[] mTmpPosition = new int[2];
|
private final int[] mTmpPosition = new int[2];
|
||||||
private Rect mTmpBounds = new Rect();
|
private Rect mTmpBounds = new Rect();
|
||||||
|
private Map<View, Rect> mButtonFullTouchableRegions = new HashMap<>();
|
||||||
|
|
||||||
private KeyButtonDrawable mBackIcon;
|
private KeyButtonDrawable mBackIcon;
|
||||||
private KeyButtonDrawable mHomeDefaultIcon;
|
private KeyButtonDrawable mHomeDefaultIcon;
|
||||||
@@ -973,9 +977,18 @@ public class NavigationBarView extends FrameLayout implements
|
|||||||
getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */));
|
getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateButtonTouchRegionCache() {
|
||||||
|
FrameLayout navBarLayout = mIsVertical
|
||||||
|
? mNavigationInflaterView.mVertical
|
||||||
|
: mNavigationInflaterView.mHorizontal;
|
||||||
|
mButtonFullTouchableRegions = ((NearestTouchFrame) navBarLayout
|
||||||
|
.findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
|
||||||
|
}
|
||||||
|
|
||||||
private Region getButtonLocations(boolean includeFloatingRotationButton,
|
private Region getButtonLocations(boolean includeFloatingRotationButton,
|
||||||
boolean inScreenSpace) {
|
boolean inScreenSpace) {
|
||||||
mTmpRegion.setEmpty();
|
mTmpRegion.setEmpty();
|
||||||
|
updateButtonTouchRegionCache();
|
||||||
updateButtonLocation(getBackButton(), inScreenSpace);
|
updateButtonLocation(getBackButton(), inScreenSpace);
|
||||||
updateButtonLocation(getHomeButton(), inScreenSpace);
|
updateButtonLocation(getHomeButton(), inScreenSpace);
|
||||||
updateButtonLocation(getRecentsButton(), inScreenSpace);
|
updateButtonLocation(getRecentsButton(), inScreenSpace);
|
||||||
@@ -999,6 +1012,12 @@ public class NavigationBarView extends FrameLayout implements
|
|||||||
if (view == null || !button.isVisible()) {
|
if (view == null || !button.isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If the button is tappable from perspective of NearestTouchFrame, then we'll
|
||||||
|
// include the regions where the tap is valid instead of just the button layout location
|
||||||
|
if (mButtonFullTouchableRegions.containsKey(view)) {
|
||||||
|
mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
updateButtonLocation(view, inScreenSpace);
|
updateButtonLocation(view, inScreenSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ package com.android.systemui.navigationbar.buttons;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -27,8 +28,13 @@ import android.widget.FrameLayout;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.systemui.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirects touches that aren't handled by any child view to the nearest
|
* Redirects touches that aren't handled by any child view to the nearest
|
||||||
@@ -36,11 +42,32 @@ import java.util.Comparator;
|
|||||||
*/
|
*/
|
||||||
public class NearestTouchFrame extends FrameLayout {
|
public class NearestTouchFrame extends FrameLayout {
|
||||||
|
|
||||||
private final ArrayList<View> mClickableChildren = new ArrayList<>();
|
private final List<View> mClickableChildren = new ArrayList<>();
|
||||||
|
private final List<View> mAttachedChildren = new ArrayList<>();
|
||||||
private final boolean mIsActive;
|
private final boolean mIsActive;
|
||||||
private final int[] mTmpInt = new int[2];
|
private final int[] mTmpInt = new int[2];
|
||||||
private final int[] mOffset = new int[2];
|
private final int[] mOffset = new int[2];
|
||||||
|
private boolean mIsVertical;
|
||||||
private View mTouchingChild;
|
private View mTouchingChild;
|
||||||
|
private final Map<View, Rect> mTouchableRegions = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* Used to sort all child views either by their left position or their top position,
|
||||||
|
* depending on if this layout is used horizontally or vertically, respectively
|
||||||
|
*/
|
||||||
|
private final Comparator<View> mChildRegionComparator =
|
||||||
|
(view1, view2) -> {
|
||||||
|
int leftTopIndex = 0;
|
||||||
|
if (mIsVertical) {
|
||||||
|
// Compare view bound's "top" values
|
||||||
|
leftTopIndex = 1;
|
||||||
|
}
|
||||||
|
view1.getLocationInWindow(mTmpInt);
|
||||||
|
int startingCoordView1 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex];
|
||||||
|
view2.getLocationInWindow(mTmpInt);
|
||||||
|
int startingCoordView2 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex];
|
||||||
|
|
||||||
|
return startingCoordView1 - startingCoordView2;
|
||||||
|
};
|
||||||
|
|
||||||
public NearestTouchFrame(Context context, AttributeSet attrs) {
|
public NearestTouchFrame(Context context, AttributeSet attrs) {
|
||||||
this(context, attrs, context.getResources().getConfiguration());
|
this(context, attrs, context.getResources().getConfiguration());
|
||||||
@@ -50,13 +77,20 @@ public class NearestTouchFrame extends FrameLayout {
|
|||||||
NearestTouchFrame(Context context, AttributeSet attrs, Configuration c) {
|
NearestTouchFrame(Context context, AttributeSet attrs, Configuration c) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
mIsActive = c.smallestScreenWidthDp < 600;
|
mIsActive = c.smallestScreenWidthDp < 600;
|
||||||
|
int[] attrsArray = new int[] {R.attr.isVertical};
|
||||||
|
TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);
|
||||||
|
mIsVertical = ta.getBoolean(0, false);
|
||||||
|
ta.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
mClickableChildren.clear();
|
mClickableChildren.clear();
|
||||||
|
mAttachedChildren.clear();
|
||||||
|
mTouchableRegions.clear();
|
||||||
addClickableChildren(this);
|
addClickableChildren(this);
|
||||||
|
cacheClosestChildLocations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,6 +99,85 @@ public class NearestTouchFrame extends FrameLayout {
|
|||||||
getLocationInWindow(mOffset);
|
getLocationInWindow(mOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates {@link #mTouchableRegions} with the regions where each clickable child is the
|
||||||
|
* closest for a given point on this layout.
|
||||||
|
*/
|
||||||
|
private void cacheClosestChildLocations() {
|
||||||
|
if (getWidth() == 0 || getHeight() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by either top or left depending on mIsVertical, then take out all children
|
||||||
|
// that are not attached to window
|
||||||
|
mClickableChildren.sort(mChildRegionComparator);
|
||||||
|
mClickableChildren.stream()
|
||||||
|
.filter(View::isAttachedToWindow)
|
||||||
|
.forEachOrdered(mAttachedChildren::add);
|
||||||
|
|
||||||
|
// Cache bounds of children
|
||||||
|
// Mark coordinates where the actual child layout resides in this frame's window
|
||||||
|
for (int i = 0; i < mAttachedChildren.size(); i++) {
|
||||||
|
View child = mAttachedChildren.get(i);
|
||||||
|
if (!child.isAttachedToWindow()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Rect childRegion = getChildsBounds(child);
|
||||||
|
|
||||||
|
// We compute closest child from this child to the previous one
|
||||||
|
if (i == 0) {
|
||||||
|
// First child, nothing to the left/top of it
|
||||||
|
if (mIsVertical) {
|
||||||
|
childRegion.top = 0;
|
||||||
|
} else {
|
||||||
|
childRegion.left = 0;
|
||||||
|
}
|
||||||
|
mTouchableRegions.put(child, childRegion);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
View previousChild = mAttachedChildren.get(i - 1);
|
||||||
|
Rect previousChildBounds = mTouchableRegions.get(previousChild);
|
||||||
|
int midPoint;
|
||||||
|
if (mIsVertical) {
|
||||||
|
int distance = childRegion.top - previousChildBounds.bottom;
|
||||||
|
midPoint = distance / 2;
|
||||||
|
childRegion.top -= midPoint;
|
||||||
|
previousChildBounds.bottom += midPoint - ((distance % 2) == 0 ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
int distance = childRegion.left - previousChildBounds.right;
|
||||||
|
midPoint = distance / 2;
|
||||||
|
childRegion.left -= midPoint;
|
||||||
|
previousChildBounds.right += midPoint - ((distance % 2) == 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == mClickableChildren.size() - 1) {
|
||||||
|
// Last child, nothing to right/bottom of it
|
||||||
|
if (mIsVertical) {
|
||||||
|
childRegion.bottom = getHeight();
|
||||||
|
} else {
|
||||||
|
childRegion.right = getWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mTouchableRegions.put(child, childRegion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setIsVertical(boolean isVertical) {
|
||||||
|
mIsVertical = isVertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rect getChildsBounds(View child) {
|
||||||
|
child.getLocationInWindow(mTmpInt);
|
||||||
|
int left = mTmpInt[0] - mOffset[0];
|
||||||
|
int top = mTmpInt[1] - mOffset[1];
|
||||||
|
int right = left + child.getWidth();
|
||||||
|
int bottom = top + child.getHeight();
|
||||||
|
return new Rect(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
private void addClickableChildren(ViewGroup group) {
|
private void addClickableChildren(ViewGroup group) {
|
||||||
final int N = group.getChildCount();
|
final int N = group.getChildCount();
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
@@ -77,47 +190,45 @@ public class NearestTouchFrame extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A Map where the key is the view object of the button and the value
|
||||||
|
* is the Rect where that button will receive a touch event if pressed. This Rect will
|
||||||
|
* usually be larger than the layout bounds for the button.
|
||||||
|
* The Rect is in screen coordinates.
|
||||||
|
*/
|
||||||
|
public Map<View, Rect> getFullTouchableChildRegions() {
|
||||||
|
Map<View, Rect> fullTouchRegions = new HashMap<>(mTouchableRegions.size());
|
||||||
|
getLocationOnScreen(mTmpInt);
|
||||||
|
for (Map.Entry<View, Rect> entry : mTouchableRegions.entrySet()) {
|
||||||
|
View child = entry.getKey();
|
||||||
|
Rect screenRegion = new Rect(entry.getValue());
|
||||||
|
screenRegion.offset(mTmpInt[0], mTmpInt[1]);
|
||||||
|
fullTouchRegions.put(child, screenRegion);
|
||||||
|
}
|
||||||
|
return fullTouchRegions;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
if (mIsActive) {
|
if (mIsActive) {
|
||||||
|
int x = (int) event.getX();
|
||||||
|
int y = (int) event.getY();
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
mTouchingChild = findNearestChild(event);
|
mTouchingChild = mClickableChildren
|
||||||
|
.stream()
|
||||||
|
.filter(View::isAttachedToWindow)
|
||||||
|
.filter(view -> mTouchableRegions.get(view).contains(x, y))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (mTouchingChild != null) {
|
if (mTouchingChild != null) {
|
||||||
event.offsetLocation(mTouchingChild.getWidth() / 2 - event.getX(),
|
event.offsetLocation(mTouchingChild.getWidth() / 2 - x,
|
||||||
mTouchingChild.getHeight() / 2 - event.getY());
|
mTouchingChild.getHeight() / 2 - y);
|
||||||
return mTouchingChild.getVisibility() == VISIBLE
|
return mTouchingChild.getVisibility() == VISIBLE
|
||||||
&& mTouchingChild.dispatchTouchEvent(event);
|
&& mTouchingChild.dispatchTouchEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onTouchEvent(event);
|
return super.onTouchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private View findNearestChild(MotionEvent event) {
|
|
||||||
if (mClickableChildren.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return mClickableChildren
|
|
||||||
.stream()
|
|
||||||
.filter(View::isAttachedToWindow)
|
|
||||||
.map(v -> new Pair<>(distance(v, event), v))
|
|
||||||
.min(Comparator.comparingInt(f -> f.first))
|
|
||||||
.map(data -> data.second)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int distance(View v, MotionEvent event) {
|
|
||||||
v.getLocationInWindow(mTmpInt);
|
|
||||||
int left = mTmpInt[0] - mOffset[0];
|
|
||||||
int top = mTmpInt[1] - mOffset[1];
|
|
||||||
int right = left + v.getWidth();
|
|
||||||
int bottom = top + v.getHeight();
|
|
||||||
|
|
||||||
int x = Math.min(Math.abs(left - (int) event.getX()),
|
|
||||||
Math.abs((int) event.getX() - right));
|
|
||||||
int y = Math.min(Math.abs(top - (int) event.getY()),
|
|
||||||
Math.abs((int) event.getY() - bottom));
|
|
||||||
|
|
||||||
return Math.max(x, y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.navigationbar.buttons;
|
package com.android.systemui.navigationbar.buttons;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper.RunWithLooper;
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@@ -39,6 +41,8 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@RunWithLooper
|
@RunWithLooper
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -51,6 +55,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
|
|||||||
Configuration c = new Configuration(mContext.getResources().getConfiguration());
|
Configuration c = new Configuration(mContext.getResources().getConfiguration());
|
||||||
c.smallestScreenWidthDp = 500;
|
c.smallestScreenWidthDp = 500;
|
||||||
mNearestTouchFrame = new NearestTouchFrame(mContext, null, c);
|
mNearestTouchFrame = new NearestTouchFrame(mContext, null, c);
|
||||||
|
mNearestTouchFrame.layout(0, 0, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -146,7 +151,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
|
|||||||
public void testVerticalSelection_Top() {
|
public void testVerticalSelection_Top() {
|
||||||
View top = mockViewAt(0, 0, 10, 10);
|
View top = mockViewAt(0, 0, 10, 10);
|
||||||
View bottom = mockViewAt(0, 20, 10, 10);
|
View bottom = mockViewAt(0, 20, 10, 10);
|
||||||
|
mNearestTouchFrame.setIsVertical(true);
|
||||||
mNearestTouchFrame.addView(top);
|
mNearestTouchFrame.addView(top);
|
||||||
mNearestTouchFrame.addView(bottom);
|
mNearestTouchFrame.addView(bottom);
|
||||||
mNearestTouchFrame.onMeasure(0, 0);
|
mNearestTouchFrame.onMeasure(0, 0);
|
||||||
@@ -162,7 +167,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
|
|||||||
public void testVerticalSelection_Bottom() {
|
public void testVerticalSelection_Bottom() {
|
||||||
View top = mockViewAt(0, 0, 10, 10);
|
View top = mockViewAt(0, 0, 10, 10);
|
||||||
View bottom = mockViewAt(0, 20, 10, 10);
|
View bottom = mockViewAt(0, 20, 10, 10);
|
||||||
|
mNearestTouchFrame.setIsVertical(true);
|
||||||
mNearestTouchFrame.addView(top);
|
mNearestTouchFrame.addView(top);
|
||||||
mNearestTouchFrame.addView(bottom);
|
mNearestTouchFrame.addView(bottom);
|
||||||
mNearestTouchFrame.onMeasure(0, 0);
|
mNearestTouchFrame.onMeasure(0, 0);
|
||||||
@@ -187,6 +192,60 @@ public class NearestTouchFrameTest extends SysuiTestCase {
|
|||||||
ev.recycle();
|
ev.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testViewMiddleChildNotAttachedCrash() {
|
||||||
|
View view1 = mockViewAt(0, 20, 10, 10);
|
||||||
|
View view2 = mockViewAt(11, 20, 10, 10);
|
||||||
|
View view3 = mockViewAt(21, 20, 10, 10);
|
||||||
|
when(view2.isAttachedToWindow()).thenReturn(false);
|
||||||
|
mNearestTouchFrame.addView(view1);
|
||||||
|
mNearestTouchFrame.addView(view2);
|
||||||
|
mNearestTouchFrame.addView(view3);
|
||||||
|
mNearestTouchFrame.onMeasure(0, 0);
|
||||||
|
|
||||||
|
MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0);
|
||||||
|
mNearestTouchFrame.onTouchEvent(ev);
|
||||||
|
verify(view2, never()).onTouchEvent(eq(ev));
|
||||||
|
ev.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCachedRegionsSplit_horizontal() {
|
||||||
|
View left = mockViewAt(0, 0, 5, 20);
|
||||||
|
View right = mockViewAt(15, 0, 5, 20);
|
||||||
|
mNearestTouchFrame.layout(0, 0, 20, 20);
|
||||||
|
|
||||||
|
mNearestTouchFrame.addView(left);
|
||||||
|
mNearestTouchFrame.addView(right);
|
||||||
|
mNearestTouchFrame.onMeasure(0, 0);
|
||||||
|
|
||||||
|
Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
|
||||||
|
assertEquals(2, childRegions.size());
|
||||||
|
Rect leftRegion = childRegions.get(left);
|
||||||
|
Rect rightRegion = childRegions.get(right);
|
||||||
|
assertEquals(new Rect(0, 0, 9, 20), leftRegion);
|
||||||
|
assertEquals(new Rect(10, 0, 20, 20), rightRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCachedRegionsSplit_vertical() {
|
||||||
|
View top = mockViewAt(0, 0, 20, 5);
|
||||||
|
View bottom = mockViewAt(0, 15, 20, 5);
|
||||||
|
mNearestTouchFrame.layout(0, 0, 20, 20);
|
||||||
|
mNearestTouchFrame.setIsVertical(true);
|
||||||
|
|
||||||
|
mNearestTouchFrame.addView(top);
|
||||||
|
mNearestTouchFrame.addView(bottom);
|
||||||
|
mNearestTouchFrame.onMeasure(0, 0);
|
||||||
|
|
||||||
|
Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
|
||||||
|
assertEquals(2, childRegions.size());
|
||||||
|
Rect topRegion = childRegions.get(top);
|
||||||
|
Rect bottomRegion = childRegions.get(bottom);
|
||||||
|
assertEquals(new Rect(0, 0, 20, 9), topRegion);
|
||||||
|
assertEquals(new Rect(0, 10, 20, 20), bottomRegion);
|
||||||
|
}
|
||||||
|
|
||||||
private View mockViewAt(int x, int y, int width, int height) {
|
private View mockViewAt(int x, int y, int width, int height) {
|
||||||
View v = spy(new View(mContext));
|
View v = spy(new View(mContext));
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user