Merge "Using BitmapRegionDecoder to only parse partial bitmap during color extraction" into ub-launcher3-master
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f372d95..9a42513 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -86,6 +86,7 @@
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -2970,7 +2971,7 @@
* new state.
*/
public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
- boolean animated, HashMap<View, Integer> layerViews) {
+ boolean animated, AnimationLayerSet layerViews) {
Workspace.State fromState = mWorkspace.getState();
Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
updateInteraction(fromState, toState);
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 6d5f951..7e84264 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -34,13 +34,12 @@
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
-import java.util.HashMap;
-
/**
* TODO: figure out what kind of tests we can write for this
*
@@ -119,9 +118,6 @@
public static final String TAG = "LSTAnimation";
- // Flags to determine how to set the layers on views before the transition animation
- public static final int BUILD_LAYER = 0;
- public static final int BUILD_AND_SET_LAYER = 1;
public static final int SINGLE_FRAME_DELAY = 16;
@Thunk Launcher mLauncher;
@@ -243,7 +239,7 @@
final View fromView = mLauncher.getWorkspace();
- final HashMap<View, Integer> layerViews = new HashMap<>();
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
// If for some reason our views aren't initialized, don't animate
boolean initialized = buttonView != null;
@@ -319,14 +315,14 @@
panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
// Play the animation
- layerViews.put(revealView, BUILD_AND_SET_LAYER);
+ layerViews.addView(revealView);
animation.play(panelAlphaAndDrift);
// Setup the animation for the content view
contentView.setVisibility(View.VISIBLE);
contentView.setAlpha(0f);
contentView.setTranslationY(revealViewToYDrift);
- layerViews.put(contentView, BUILD_AND_SET_LAYER);
+ layerViews.addView(contentView);
// Create the individual animators
ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
@@ -365,13 +361,6 @@
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
- // Disable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
// This can hold unnecessary references to views.
cleanupAnimation();
pCb.onTransitionComplete();
@@ -393,16 +382,6 @@
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
- // Enable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
- v.buildLayer();
- }
- }
-
// Focus the new view
toView.requestFocus();
@@ -411,25 +390,19 @@
};
toView.bringToFront();
toView.setVisibility(View.VISIBLE);
+
+ animation.addListener(layerViews);
toView.post(startAnimRunnable);
mCurrentAnimation = animation;
} else if (animType == PULLUP) {
// We are animating the content view alpha, so ensure we have a layer for it
- layerViews.put(contentView, BUILD_AND_SET_LAYER);
+ layerViews.addView(contentView);
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
-
- // Disable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
cleanupAnimation();
pCb.onTransitionComplete();
}
@@ -450,21 +423,12 @@
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
- // Enable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
- v.buildLayer();
- }
- }
-
toView.requestFocus();
stateAnimation.start();
}
};
mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(layerViews);
if (shouldPost) {
toView.post(startAnimRunnable);
} else {
@@ -479,7 +443,7 @@
private void playCommonTransitionAnimations(
Workspace.State toWorkspaceState, View fromView, View toView,
boolean animated, boolean initialized, AnimatorSet animation,
- HashMap<View, Integer> layerViews) {
+ AnimationLayerSet layerViews) {
// Create the workspace animation.
// NOTE: this call apparently also sets the state for the workspace if !animated
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
@@ -594,10 +558,8 @@
final Workspace.State toWorkspaceState, final boolean animated,
final Runnable onCompleteRunnable) {
final View fromWorkspace = mLauncher.getWorkspace();
- final HashMap<View, Integer> layerViews = new HashMap<>();
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- final int revealDuration = mLauncher.getResources()
- .getInteger(R.integer.config_overlayRevealTime);
// Cancel the current animation
cancelAnimation();
@@ -622,16 +584,6 @@
return;
dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
-
- // Enable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
- v.buildLayer();
- }
- }
stateAnimation.start();
}
};
@@ -645,17 +597,11 @@
onCompleteRunnable.run();
}
- // Disable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
// This can hold unnecessary references to views.
cleanupAnimation();
}
});
+ stateAnimation.addListener(layerViews);
fromWorkspace.post(startAnimRunnable);
mCurrentAnimation = animation;
} else /* if (!animated) */ {
@@ -692,7 +638,7 @@
final View revealView = fromView.getRevealView();
final View contentView = fromView.getContentView();
- final HashMap<View, Integer> layerViews = new HashMap<>();
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
// If for some reason our views aren't initialized, don't animate
boolean initialized = buttonView != null;
@@ -735,7 +681,7 @@
revealView.setVisibility(View.VISIBLE);
revealView.setAlpha(1f);
revealView.setTranslationY(0);
- layerViews.put(revealView, BUILD_AND_SET_LAYER);
+ layerViews.addView(revealView);
// Calculate the final animation values
final float revealViewToXDrift;
@@ -783,7 +729,7 @@
}
// Setup the animation for the content view
- layerViews.put(contentView, BUILD_AND_SET_LAYER);
+ layerViews.addView(contentView);
// Create the individual animators
ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
@@ -843,13 +789,6 @@
onCompleteRunnable.run();
}
- // Disable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
// Reset page transforms
if (contentView != null) {
contentView.setTranslationX(0);
@@ -874,24 +813,15 @@
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
-
- // Enable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
- v.buildLayer();
- }
- }
stateAnimation.start();
}
};
mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(layerViews);
fromView.post(startAnimRunnable);
} else if (animType == PULLUP) {
// We are animating the content view alpha, so ensure we have a layer for it
- layerViews.put(contentView, BUILD_AND_SET_LAYER);
+ layerViews.addView(contentView);
animation.addListener(new AnimatorListenerAdapter() {
boolean canceled = false;
@@ -910,13 +840,6 @@
onCompleteRunnable.run();
}
- // Disable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
cleanupAnimation();
pCb.onTransitionComplete();
}
@@ -939,22 +862,13 @@
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
- // Enable all necessary layers
- for (View v : layerViews.keySet()) {
- if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
- v.buildLayer();
- }
- }
-
// Focus the new view
toView.requestFocus();
stateAnimation.start();
}
};
mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(layerViews);
if (shouldPost) {
fromView.post(startAnimRunnable);
} else {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9ddc14f..568a52f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -59,7 +59,6 @@
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Set;
@@ -82,6 +81,9 @@
private static final int[] sLoc0 = new int[2];
private static final int[] sLoc1 = new int[2];
+ private static final float[] sPoint = new float[2];
+ private static final Matrix sMatrix = new Matrix();
+ private static final Matrix sInverseMatrix = new Matrix();
public static final boolean ATLEAST_NOUGAT_MR1 =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
@@ -160,68 +162,52 @@
*/
public static float getDescendantCoordRelativeToAncestor(
View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
- float[] pt = {coord[0], coord[1]};
+ sPoint[0] = coord[0];
+ sPoint[1] = coord[1];
+
float scale = 1.0f;
View v = descendant;
while(v != ancestor && v != null) {
// For TextViews, scroll has a meaning which relates to the text position
// which is very strange... ignore the scroll.
if (v != descendant || includeRootScroll) {
- pt[0] -= v.getScrollX();
- pt[1] -= v.getScrollY();
+ sPoint[0] -= v.getScrollX();
+ sPoint[1] -= v.getScrollY();
}
- v.getMatrix().mapPoints(pt);
- pt[0] += v.getLeft();
- pt[1] += v.getTop();
+ v.getMatrix().mapPoints(sPoint);
+ sPoint[0] += v.getLeft();
+ sPoint[1] += v.getTop();
scale *= v.getScaleX();
v = (View) v.getParent();
}
- coord[0] = Math.round(pt[0]);
- coord[1] = Math.round(pt[1]);
+ coord[0] = Math.round(sPoint[0]);
+ coord[1] = Math.round(sPoint[1]);
return scale;
}
/**
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
*/
- public static float mapCoordInSelfToDescendent(View descendant, View root,
- int[] coord) {
- ArrayList<View> ancestorChain = new ArrayList<View>();
-
- float[] pt = {coord[0], coord[1]};
-
+ public static void mapCoordInSelfToDescendant(View descendant, View root, int[] coord) {
+ sMatrix.reset();
View v = descendant;
while(v != root) {
- ancestorChain.add(v);
+ sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
+ sMatrix.postConcat(v.getMatrix());
+ sMatrix.postTranslate(v.getLeft(), v.getTop());
v = (View) v.getParent();
}
- ancestorChain.add(root);
+ sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
+ sMatrix.invert(sInverseMatrix);
- float scale = 1.0f;
- Matrix inverse = new Matrix();
- int count = ancestorChain.size();
- for (int i = count - 1; i >= 0; i--) {
- View ancestor = ancestorChain.get(i);
- View next = i > 0 ? ancestorChain.get(i-1) : null;
-
- pt[0] += ancestor.getScrollX();
- pt[1] += ancestor.getScrollY();
-
- if (next != null) {
- pt[0] -= next.getLeft();
- pt[1] -= next.getTop();
- next.getMatrix().invert(inverse);
- inverse.mapPoints(pt);
- scale *= next.getScaleX();
- }
- }
-
- coord[0] = (int) Math.round(pt[0]);
- coord[1] = (int) Math.round(pt[1]);
- return scale;
+ sPoint[0] = coord[0];
+ sPoint[1] = coord[1];
+ sInverseMatrix.mapPoints(sPoint);
+ coord[0] = Math.round(sPoint[0]);
+ coord[1] = Math.round(sPoint[1]);
}
/**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 173f4d1..95a5ee2 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,6 +60,7 @@
import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -86,7 +87,6 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
/**
@@ -2029,7 +2029,7 @@
* to that new state.
*/
public Animator setStateWithAnimation(State toState, boolean animated,
- HashMap<View, Integer> layerViews) {
+ AnimationLayerSet layerViews) {
// Create the animation to the new state
AnimatorSet workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
toState, animated, layerViews);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 598ba74..1cf4b39 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -30,12 +30,11 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.Thunk;
-import java.util.HashMap;
-
/**
* A convenience class to update a view's visibility state after an alpha animation.
*/
@@ -226,7 +225,7 @@
}
public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
- boolean animated, HashMap<View, Integer> layerViews) {
+ boolean animated, AnimationLayerSet layerViews) {
AccessibilityManager am = (AccessibilityManager)
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
final boolean accessibilityEnabled = am.isEnabled();
@@ -262,8 +261,7 @@
* Starts a transition animation for the workspace.
*/
private void animateWorkspace(final TransitionStates states, final boolean animated,
- final int duration, final HashMap<View, Integer> layerViews,
- final boolean accessibilityEnabled) {
+ final int duration, AnimationLayerSet layerViews, final boolean accessibilityEnabled) {
// Cancel existing workspace animations and create a new animator set if requested
cancelAnimation();
if (animated) {
@@ -396,12 +394,10 @@
// For animation optimization, we may need to provide the Launcher transition
// with a set of views on which to force build and manage layers in certain scenarios.
- layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
- layerViews.put(qsbContainer, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
- layerViews.put(mLauncher.getHotseat(),
- LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
- layerViews.put(mWorkspace.getPageIndicator(),
- LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+ layerViews.addView(overviewPanel);
+ layerViews.addView(qsbContainer);
+ layerViews.addView(mLauncher.getHotseat());
+ layerViews.addView(mWorkspace.getPageIndicator());
if (states.workspaceToOverview) {
hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index b5aed0d..34d8a83 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -193,7 +193,7 @@
int[] point = new int[2];
point[0] = (int) ev.getX();
point[1] = (int) ev.getY();
- Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
+ Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point);
// IF the MotionEvent is inside the search box, and the container keeps on receiving
// touch input, container should move down.
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/HeaderElevationController.java
index ce9837c..7941ac5 100644
--- a/src/com/android/launcher3/allapps/HeaderElevationController.java
+++ b/src/com/android/launcher3/allapps/HeaderElevationController.java
@@ -7,6 +7,7 @@
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
+import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -14,6 +15,7 @@
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
/**
* Helper class for controlling the header elevation in response to RecyclerView scroll.
@@ -96,16 +98,16 @@
public void getOutline(View view, Outline outline) {
final View parent = (View) mHeader.getParent();
+ DisplayMetrics metrics = new DisplayMetrics();
+ view.getDisplay().getMetrics(metrics);
+ int offset = Utilities.pxFromDp(mMaxElevation, metrics);
+
final int left = parent.getLeft(); // Use the parent to account for offsets
final int top = view.getTop();
final int right = left + view.getWidth();
final int bottom = view.getBottom();
- outline.setRect(
- left - (int) mMaxElevation,
- top - (int) mMaxElevation,
- right + (int) mMaxElevation,
- bottom);
+ outline.setRect(left - offset, top - offset, right + offset, bottom);
}
};
mHeader.setOutlineProvider(vop);
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
new file mode 100644
index 0000000..42706ff
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimationLayerSet.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.view.View;
+
+import java.util.HashSet;
+
+/**
+ * Helper class to automatically build view hardware layers for the duration of an animation.
+ */
+public class AnimationLayerSet extends AnimatorListenerAdapter {
+
+ private final HashSet<View> mViews = new HashSet<>();
+
+ public void addView(View v) {
+ mViews.add(v);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Enable all necessary layers
+ for (View v : mViews) {
+ v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ if (v.isAttachedToWindow() && v.getVisibility() == View.VISIBLE) {
+ v.buildLayer();
+ }
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ for (View v : mViews) {
+ v.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 9618779..9de4452 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -480,8 +480,8 @@
/**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
*/
- public float mapCoordInSelfToDescendant(View descendant, int[] coord) {
- return Utilities.mapCoordInSelfToDescendent(descendant, this, coord);
+ public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
+ Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
}
public void getViewRectRelativeToSelf(View v, Rect r) {