diff options
5 files changed, 187 insertions, 301 deletions
diff --git a/res/layout/grant_permissions.xml b/res/layout/grant_permissions.xml index 463f0ccd..185bade5 100644 --- a/res/layout/grant_permissions.xml +++ b/res/layout/grant_permissions.xml @@ -17,71 +17,74 @@ <com.android.packageinstaller.permission.ui.ManualLayoutFrame xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" - android:layout_height="fill_parent" > + android:layout_height="fill_parent" + android:clipChildren="false"> <LinearLayout android:id="@+id/dialog_container" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:paddingTop="24dip" - android:paddingBottom="8dip" - android:paddingStart="22dip" - android:paddingEnd="16dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:orientation="vertical"> <FrameLayout android:id="@+id/desc_container" android:layout_width="match_parent" - android:layout_height="wrap_content" > + android:layout_height="wrap_content" + android:paddingTop="24dip" + android:paddingStart="22dip" + android:paddingEnd="16dip" + android:background="?android:attr/colorBackgroundFloating"> <include layout="@layout/permission_description" /> </FrameLayout> - <CheckBox - android:id="@+id/do_not_ask_checkbox" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="16dip" - android:text="@string/never_ask_again" - style="?android:attr/textAppearanceSmall" - android:visibility="gone"> - </CheckBox> - - <com.android.packageinstaller.permission.ui.ButtonBarLayout - android:id="@+id/button_group" + <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" - android:paddingStart="2dip" - android:paddingTop="16dip"> - - <TextView - android:id="@+id/current_page_text" - android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="8dip" + android:paddingStart="22dip" + android:paddingEnd="16dip" + android:background="?android:attr/colorBackgroundFloating"> + + <CheckBox + android:id="@+id/do_not_ask_checkbox" + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_gravity="start|bottom" - android:paddingTop="4dp" - android:paddingBottom="4dp" - android:paddingEnd="12dp" - android:singleLine="true" + android:layout_marginTop="16dip" + android:text="@string/never_ask_again" style="?android:attr/textAppearanceSmall" - android:textColor="?android:attr/textColorSecondary" - android:visibility="invisible"> - </TextView> - - <Space - android:id="@*android:id/spacer" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" - android:visibility="invisible" > - </Space> - - <LinearLayout - android:layout_width="wrap_content" + android:visibility="gone"> + </CheckBox> + + <com.android.packageinstaller.permission.ui.ButtonBarLayout + android:id="@+id/button_group" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="end" - android:orientation="horizontal" > + android:orientation="horizontal" + android:paddingStart="2dip" + android:paddingTop="16dip"> + + <TextView + android:id="@+id/current_page_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingEnd="12dp" + android:singleLine="true" + style="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" + android:visibility="invisible"> + </TextView> + + <Space + android:id="@*android:id/spacer" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1" + android:visibility="invisible" > + </Space> <Button android:id="@+id/permission_deny_button" @@ -100,9 +103,9 @@ android:text="@string/grant_dialog_button_allow" > </Button> - </LinearLayout> + </com.android.packageinstaller.permission.ui.ButtonBarLayout> - </com.android.packageinstaller.permission.ui.ButtonBarLayout> + </LinearLayout> </LinearLayout> diff --git a/res/values/themes.xml b/res/values/themes.xml index 6f325d2b..db4b744e 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -28,6 +28,9 @@ <style name="GrantPermissions" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar"> + <item name="*android:windowFixedHeightMajor">100%</item> + <item name="*android:windowFixedHeightMinor">100%</item> + <item name="android:windowBackground">@android:color/transparent</item> </style> </resources> diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java index 3286659d..416bb9ca 100644 --- a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java @@ -46,8 +46,8 @@ import com.android.packageinstaller.R; import com.android.packageinstaller.permission.model.AppPermissionGroup; import com.android.packageinstaller.permission.model.AppPermissions; import com.android.packageinstaller.permission.model.Permission; +import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl; import com.android.packageinstaller.permission.utils.SafetyNetLogger; -import com.android.packageinstaller.permission.utils.Utils; import libcore.util.EmptyArray; import java.util.ArrayList; @@ -190,15 +190,16 @@ public class GrantPermissionsActivity extends OverlayTouchActivity @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - // This is just gross - we need to relayout the window as - // dialog width may be different in landscape vs portrait - // which affect the min window height needed to show all - // content. We have to re-add the window to force it to be - // resized if needed. The way the permission grant dialog - // is implemented is not correct and has to be rewritten. + // We need to relayout the window as dialog width may be + // different in landscape vs portrait which affect the min + // window height needed to show all content. We have to + // re-add the window to force it to be resized if needed. View decor = getWindow().getDecorView(); getWindowManager().removeViewImmediate(decor); getWindowManager().addView(decor, decor.getLayoutParams()); + if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) { + ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged(); + } } @Override diff --git a/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java b/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java index 1af400b3..a20c9523 100644 --- a/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java +++ b/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java @@ -17,27 +17,20 @@ package com.android.packageinstaller.permission.ui; import android.content.Context; import android.util.AttributeSet; -import android.widget.FrameLayout; +import android.view.View; +import android.view.ViewGroup; -/** - * Allows one standard layout pass, but afterwards holds getMeasuredHeight constant, - * however still allows drawing larger at the size needed by its children. This allows - * a dialog to tell the window the height is constant (with keeps its position constant) - * but allows the view to grow downwards for animation. - */ -public class ManualLayoutFrame extends FrameLayout { - private int mDesiredHeight; - private int mHeight; +public class ManualLayoutFrame extends ViewGroup { + private int mContentBottom; private int mWidth; public ManualLayoutFrame(Context context, AttributeSet attrs) { super(context, attrs); - setClipChildren(false); - setClipToPadding(false); } - public int getLayoutHeight() { - return mDesiredHeight; + public void onConfigurationChanged() { + mContentBottom = 0; + mWidth = 0; } @Override @@ -53,38 +46,28 @@ public class ManualLayoutFrame extends FrameLayout { newWidth = MeasureSpec.getSize(widthMeasureSpec); } break; } - // If the width changes we have to re-evaluate the height if (newWidth != mWidth) { mWidth = newWidth; - mHeight = 0; } widthMeasureSpec = MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY); } - - // Let the content measure how much it needs to be fully shown - heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - mDesiredHeight = getMeasuredHeight(); - if (mHeight == 0 && mDesiredHeight != 0) { - // Record the first non-zero width and height, this will be the height henceforth. - mHeight = mDesiredHeight; + if (mWidth == 0) { mWidth = getMeasuredWidth(); } - if (mHeight != 0) { - // Always report the same height - setMeasuredDimension(getMeasuredWidth(), mHeight); - } + + measureChild(getChildAt(0), widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (mDesiredHeight != 0) { - // Draw at height we expect to be. - setBottom(getTop() + mDesiredHeight); - bottom = top + mDesiredHeight; + View content = getChildAt(0); + if (mContentBottom == 0) { + mContentBottom = (getMeasuredHeight() + content.getMeasuredHeight()) / 2; } - super.onLayout(changed, left, top, right, bottom); + final int contentLeft = (getMeasuredWidth() - content.getMeasuredWidth()) / 2; + final int contentTop = mContentBottom - content.getMeasuredHeight(); + final int contentRight = contentLeft + content.getMeasuredWidth(); + content.layout(contentLeft, contentTop, contentRight, mContentBottom); } } diff --git a/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java b/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java index c34a62a3..f2b0912d 100644 --- a/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java +++ b/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java @@ -16,22 +16,14 @@ package com.android.packageinstaller.permission.ui.handheld; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; -import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.ViewRootImpl; import android.view.WindowManager.LayoutParams; -import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.Button; @@ -44,8 +36,6 @@ import com.android.packageinstaller.permission.ui.ButtonBarLayout; import com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler; import com.android.packageinstaller.permission.ui.ManualLayoutFrame; -import java.util.ArrayList; - public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler, OnClickListener { @@ -58,14 +48,8 @@ public final class GrantPermissionsViewHandlerImpl public static final String ARG_GROUP_DO_NOT_ASK_CHECKED = "ARG_GROUP_DO_NOT_ASK_CHECKED"; // Animation parameters. - private static final long SIZE_START_DELAY = 300; - private static final long SIZE_START_LENGTH = 233; - private static final long FADE_OUT_START_DELAY = 300; - private static final long FADE_OUT_START_LENGTH = 217; - private static final long TRANSLATE_START_DELAY = 367; - private static final long TRANSLATE_LENGTH = 317; - private static final long GROUP_UPDATE_DELAY = 400; - private static final long DO_NOT_ASK_CHECK_DELAY = 450; + private static final long OUT_DURATION = 200; + private static final long IN_DURATION = 300; private final Context mContext; @@ -85,22 +69,13 @@ public final class GrantPermissionsViewHandlerImpl private CheckBox mDoNotAskCheckbox; private Button mAllowButton; - private ArrayList<ViewHeightController> mHeightControllers; private ManualLayoutFrame mRootView; // Needed for animation private ViewGroup mDescContainer; private ViewGroup mCurrentDesc; - private ViewGroup mNextDesc; - private ViewGroup mDialogContainer; - - private final Runnable mUpdateGroup = new Runnable() { - @Override - public void run() { - updateGroup(); - } - }; + private ButtonBarLayout mButtonBar; public GrantPermissionsViewHandlerImpl(Context context) { mContext = context; @@ -159,170 +134,148 @@ public final class GrantPermissionsViewHandlerImpl } } - private void animateToPermission() { - if (mHeightControllers == null) { - // We need to manually control the height of any views heigher than the root that - // we inflate. Find all the views up to the root and create ViewHeightControllers for - // them. - mHeightControllers = new ArrayList<>(); - ViewRootImpl viewRoot = mRootView.getViewRootImpl(); - ViewParent v = mRootView.getParent(); - addHeightController(mDialogContainer); - addHeightController(mRootView); - while (v != viewRoot) { - addHeightController((View) v); - v = v.getParent(); - } - // On the heighest level view, we want to setTop rather than setBottom to control the - // height, this way the dialog will grow up rather than down. - ViewHeightController realRootView = - mHeightControllers.get(mHeightControllers.size() - 1); - realRootView.setControlTop(true); - } - - // Grab the current height/y positions, then wait for the layout to change, - // so we can get the end height/y positions. - final SparseArray<Float> startPositions = getViewPositions(); - final int startHeight = mRootView.getLayoutHeight(); - mRootView.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - mRootView.removeOnLayoutChangeListener(this); - SparseArray<Float> endPositions = getViewPositions(); - int endHeight = mRootView.getLayoutHeight(); - if (startPositions.get(R.id.do_not_ask_checkbox) == 0 - && endPositions.get(R.id.do_not_ask_checkbox) != 0) { - // If the checkbox didn't have a position before but has one now then set - // the start position to the end position because it just became visible. - startPositions.put(R.id.do_not_ask_checkbox, - endPositions.get(R.id.do_not_ask_checkbox)); - } - animateYPos(startPositions, endPositions, endHeight - startHeight); - } - }); + public void onConfigurationChanged() { + mRootView.onConfigurationChanged(); + } + private void animateOldContent(Runnable callback) { // Fade out old description group and scale out the icon for it. Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_linear_in); + + // Icon scale to zero mIconView.animate() .scaleX(0) .scaleY(0) - .setStartDelay(FADE_OUT_START_DELAY) - .setDuration(FADE_OUT_START_LENGTH) + .setDuration(OUT_DURATION) .setInterpolator(interpolator) .start(); + + // Description fade out mCurrentDesc.animate() .alpha(0) - .setStartDelay(FADE_OUT_START_DELAY) - .setDuration(FADE_OUT_START_LENGTH) + .setDuration(OUT_DURATION) .setInterpolator(interpolator) - .setListener(null) + .withEndAction(callback) .start(); - // Update the index of the permission after the animations have started. - mCurrentGroupView.getHandler().postDelayed(mUpdateGroup, GROUP_UPDATE_DELAY); + // Checkbox fade out if needed + if (!mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE) { + mDoNotAskCheckbox.animate() + .alpha(0) + .setDuration(OUT_DURATION) + .setInterpolator(interpolator) + .start(); + } + } - // Add the new description and translate it in. - mNextDesc = (ViewGroup) LayoutInflater.from(mContext).inflate( + private void attachNewContent(final Runnable callback) { + mCurrentDesc = (ViewGroup) LayoutInflater.from(mContext).inflate( R.layout.permission_description, mDescContainer, false); + mDescContainer.removeAllViews(); + mDescContainer.addView(mCurrentDesc); + + mDialogContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + mDialogContainer.removeOnLayoutChangeListener(this); + + // Prepare new content to the right to be moved in + final int containerWidth = mDescContainer.getWidth(); + mCurrentDesc.setTranslationX(containerWidth); + + // How much scale for the dialog to appear the same? + final int oldDynamicHeight = oldBottom - oldTop - mButtonBar.getHeight(); + final float scaleY = (float) oldDynamicHeight / mDescContainer.getHeight(); + + // How much to translate for the dialog to appear the same? + final int translationCompensatingScale = (int) (scaleY + * mDescContainer.getHeight() - mDescContainer.getHeight()) / 2; + final int translationY = (oldTop - top) + translationCompensatingScale; + + // Animate to the current layout + mDescContainer.setScaleY(scaleY); + mDescContainer.setTranslationY(translationY); + mDescContainer.animate() + .translationY(0) + .scaleY(1.0f) + .setInterpolator(AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.linear_out_slow_in)) + .setDuration(IN_DURATION) + .withEndAction(callback) + .start(); + } + } + ); - mMessageView = (TextView) mNextDesc.findViewById(R.id.permission_message); - mIconView = (ImageView) mNextDesc.findViewById(R.id.permission_icon); - updateDescription(); - - int width = mDescContainer.getRootView().getWidth(); - mDescContainer.addView(mNextDesc); - mNextDesc.setTranslationX(width); - - final View oldDesc = mCurrentDesc; - // Remove the old view from the description, so that we can shrink if necessary. - mDescContainer.removeView(oldDesc); - oldDesc.setPadding(mDescContainer.getLeft(), mDescContainer.getTop(), - mRootView.getRight() - mDescContainer.getRight(), 0); - mRootView.addView(oldDesc); + mMessageView = (TextView) mCurrentDesc.findViewById(R.id.permission_message); + mIconView = (ImageView) mCurrentDesc.findViewById(R.id.permission_icon); - mCurrentDesc = mNextDesc; - mNextDesc.animate() - .translationX(0) - .setStartDelay(TRANSLATE_START_DELAY) - .setDuration(TRANSLATE_LENGTH) - .setInterpolator(AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.linear_out_slow_in)) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // This is the longest animation, when it finishes, we are done. - mRootView.removeView(oldDesc); - } - }) - .start(); + final boolean doNotAskWasShown = mDoNotAskCheckbox.getVisibility() == View.VISIBLE; - boolean visibleBefore = mDoNotAskCheckbox.getVisibility() == View.VISIBLE; + updateDescription(); + updateGroup(); updateDoNotAskCheckBox(); - boolean visibleAfter = mDoNotAskCheckbox.getVisibility() == View.VISIBLE; - if (visibleBefore != visibleAfter) { - Animation anim = AnimationUtils.loadAnimation(mContext, - visibleAfter ? android.R.anim.fade_in : android.R.anim.fade_out); - anim.setStartOffset(visibleAfter ? DO_NOT_ASK_CHECK_DELAY : 0); - mDoNotAskCheckbox.startAnimation(anim); + + if (!doNotAskWasShown && mShowDonNotAsk) { + mDoNotAskCheckbox.setAlpha(0); } } - private void addHeightController(View v) { - ViewHeightController heightController = new ViewHeightController(v); - heightController.setHeight(v.getHeight()); - mHeightControllers.add(heightController); - } + private void animateNewContent() { + Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.linear_out_slow_in); - private SparseArray<Float> getViewPositions() { - SparseArray<Float> locMap = new SparseArray<>(); - final int N = mDialogContainer.getChildCount(); - for (int i = 0; i < N; i++) { - View child = mDialogContainer.getChildAt(i); - if (child.getId() <= 0) { - // Only track views with ids. - continue; - } - locMap.put(child.getId(), child.getY()); + // Description slide in + mCurrentDesc.animate() + .translationX(0) + .setDuration(IN_DURATION) + .setInterpolator(interpolator) + .start(); + + // Checkbox fade in if needed + if (mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE + && mDoNotAskCheckbox.getAlpha() < 1.0f) { + mDoNotAskCheckbox.setAlpha(0); + mDoNotAskCheckbox.animate() + .alpha(1.0f) + .setDuration(IN_DURATION) + .setInterpolator(interpolator) + .start(); } - return locMap; } - private void animateYPos(SparseArray<Float> startPositions, SparseArray<Float> endPositions, - int heightDiff) { - final int N = startPositions.size(); - for (int i = 0; i < N; i++) { - int key = startPositions.keyAt(i); - float start = startPositions.get(key); - float end = endPositions.get(key); - if (start != end) { - final View child = mDialogContainer.findViewById(key); - child.setTranslationY(start - end); - child.animate() - .setStartDelay(SIZE_START_DELAY) - .setDuration(SIZE_START_LENGTH) - .translationY(0) - .start(); + private void animateToPermission() { + // Remove the old content + animateOldContent(new Runnable() { + @Override + public void run() { + // Add the new content + attachNewContent(new Runnable() { + @Override + public void run() { + // Animate the new content + animateNewContent(); + } + }); } - } - for (int i = 0; i < mHeightControllers.size(); i++) { - mHeightControllers.get(i).animateAddHeight(heightDiff); - } + }); } @Override public View createView() { mRootView = (ManualLayoutFrame) LayoutInflater.from(mContext) .inflate(R.layout.grant_permissions, null); - ((ButtonBarLayout) mRootView.findViewById(R.id.button_group)).setAllowStacking(true); - mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container); + mButtonBar = (ButtonBarLayout) mRootView.findViewById(R.id.button_group); + mButtonBar.setAllowStacking(true); mMessageView = (TextView) mRootView.findViewById(R.id.permission_message); mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon); mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text); mDoNotAskCheckbox = (CheckBox) mRootView.findViewById(R.id.do_not_ask_checkbox); mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button); + mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container); mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container); mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root); @@ -400,61 +353,4 @@ public final class GrantPermissionsViewHandlerImpl mResultListener.onPermissionGrantResult(mGroupName, false, doNotAskAgain); } } - - /** - * Manually controls the height of a view through getBottom/setTop. Also listens - * for layout changes and sets the height again to be sure it doesn't change. - */ - private static final class ViewHeightController implements OnLayoutChangeListener { - private final View mView; - private int mHeight; - private int mNextHeight; - private boolean mControlTop; - private ObjectAnimator mAnimator; - - public ViewHeightController(View view) { - mView = view; - mView.addOnLayoutChangeListener(this); - } - - public void setControlTop(boolean controlTop) { - mControlTop = controlTop; - } - - public void animateAddHeight(int heightDiff) { - if (heightDiff != 0) { - if (mNextHeight == 0) { - mNextHeight = mHeight; - } - mNextHeight += heightDiff; - if (mAnimator != null) { - mAnimator.cancel(); - } - mAnimator = ObjectAnimator.ofInt(this, "height", mHeight, mNextHeight); - mAnimator.setStartDelay(SIZE_START_DELAY); - mAnimator.setDuration(SIZE_START_LENGTH); - mAnimator.start(); - } - } - - public void setHeight(int height) { - mHeight = height; - updateHeight(); - } - - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - // Ensure that the height never changes. - updateHeight(); - } - - private void updateHeight() { - if (mControlTop) { - mView.setTop(mView.getBottom() - mHeight); - } else { - mView.setBottom(mView.getTop() + mHeight); - } - } - } } |