From dc500de99ccb42393d6dae3ecf4748b560775019 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Fri, 6 Mar 2015 12:06:31 -0800 Subject: Remove OverlappingPaneLayout. - Remove the OverlappingPaneLayout, which is no longer needed since the recents card has been removed. - Known Issue: SearchBar is no longer hidden on scroll. - Known Issue: shouldShowActionBar does not return correct value. I plan to fix these KIs in a follow up CL, but going for incremental changes for now to try to keep it apparent what is being done. Bug: 19372817 Change-Id: I9ae1ca19820c87f252863074a5bcab6923b03558 --- .../dialer/widget/OverlappingPaneLayout.java | 1358 -------------------- 1 file changed, 1358 deletions(-) delete mode 100644 src/com/android/dialer/widget/OverlappingPaneLayout.java (limited to 'src/com/android/dialer/widget') diff --git a/src/com/android/dialer/widget/OverlappingPaneLayout.java b/src/com/android/dialer/widget/OverlappingPaneLayout.java deleted file mode 100644 index 167b849f2..000000000 --- a/src/com/android/dialer/widget/OverlappingPaneLayout.java +++ /dev/null @@ -1,1358 +0,0 @@ -/* - * Copyright (C) 2012 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.dialer.widget; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.v4.view.AccessibilityDelegateCompat; -import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; - -/** - * A custom layout that aligns its child views vertically as two panes, and allows for the bottom - * pane to be dragged upwards to overlap and hide the top pane. This layout is adapted from - * {@link android.support.v4.widget.SlidingPaneLayout}. - */ -public class OverlappingPaneLayout extends ViewGroup { - private static final String TAG = "SlidingPaneLayout"; - private static final boolean DEBUG = false; - - /** - * Default size of the overhang for a pane in the open state. - * At least this much of a sliding pane will remain visible. - * This indicates that there is more content available and provides - * a "physical" edge to grab to pull it closed. - */ - private static final int DEFAULT_OVERHANG_SIZE = 32; // dp; - - /** - * If no fade color is given by default it will fade to 80% gray. - */ - private static final int DEFAULT_FADE_COLOR = 0xcccccccc; - - /** - * Minimum velocity that will be detected as a fling - */ - private static final int MIN_FLING_VELOCITY = 400; // dips per second - - /** - * The size of the overhang in pixels. - * This is the minimum section of the sliding panel that will - * be visible in the open state to allow for a closing drag. - */ - private final int mOverhangSize; - - /** - * True if a panel can slide with the current measurements - */ - private boolean mCanSlide; - - /** - * The child view that can slide, if any. - */ - private View mSlideableView; - - /** - * The view that can be used to start the drag with. - */ - private View mCapturableView; - - /** - * How far the panel is offset from its closed position. - * range [0, 1] where 0 = closed, 1 = open. - */ - private float mSlideOffset; - - /** - * How far the panel is offset from its closed position, in pixels. - * range [0, {@link #mSlideRange}] where 0 is completely closed. - */ - private int mSlideOffsetPx; - - /** - * How far in pixels the slideable panel may move. - */ - private int mSlideRange; - - /** - * A panel view is locked into internal scrolling or another condition that - * is preventing a drag. - */ - private boolean mIsUnableToDrag; - - /** - * Tracks whether or not a child view is in the process of a nested scroll. - */ - private boolean mIsInNestedScroll; - - /** - * Indicates that the layout is currently in the process of a nested pre-scroll operation where - * the child scrolling view is being dragged downwards. - */ - private boolean mInNestedPreScrollDownwards; - - /** - * Indicates that the layout is currently in the process of a nested pre-scroll operation where - * the child scrolling view is being dragged upwards. - */ - private boolean mInNestedPreScrollUpwards; - - /** - * Indicates that the layout is currently in the process of a fling initiated by a pre-fling - * from the child scrolling view. - */ - private boolean mIsInNestedFling; - - /** - * Indicates the direction of the pre fling. We need to store this information since - * OverScoller doesn't expose the direction of its velocity. - */ - private boolean mInUpwardsPreFling; - - /** - * Stores an offset used to represent a point somewhere in between the panel's fully closed - * state and fully opened state where the panel can be temporarily pinned or opened up to - * during scrolling. - */ - private int mIntermediateOffset = 0; - - private float mInitialMotionX; - private float mInitialMotionY; - - private PanelSlideCallbacks mPanelSlideCallbacks; - - private final ViewDragHelper mDragHelper; - - /** - * Stores whether or not the pane was open the last time it was slideable. - * If open/close operations are invoked this state is modified. Used by - * instance state save/restore. - */ - private boolean mPreservedOpenState; - private boolean mFirstLayout = true; - - private final Rect mTmpRect = new Rect(); - - /** - * How many dips we need to scroll past a position before we can snap to the next position - * on release. Using this prevents accidentally snapping to positions. - * - * This is needed since vertical nested scrolling can be passed to this class even if the - * vertical scroll is less than the the nested list's touch slop. - */ - private final int mReleaseScrollSlop; - - /** - * Callbacks for interacting with sliding panes. - */ - public interface PanelSlideCallbacks { - /** - * Called when a sliding pane's position changes. - * @param panel The child view that was moved - * @param slideOffset The new offset of this sliding pane within its range, from 0-1 - */ - public void onPanelSlide(View panel, float slideOffset); - /** - * Called when a sliding pane becomes slid completely open. The pane may or may not - * be interactive at this point depending on how much of the pane is visible. - * @param panel The child view that was slid to an open position, revealing other panes - */ - public void onPanelOpened(View panel); - - /** - * Called when a sliding pane becomes slid completely closed. The pane is now guaranteed - * to be interactive. It may now obscure other views in the layout. - * @param panel The child view that was slid to a closed position - */ - public void onPanelClosed(View panel); - - /** - * Called when a sliding pane is flung as far open/closed as it can be. - * @param velocityY Velocity of the panel once its fling goes as far as it can. - */ - public void onPanelFlingReachesEdge(int velocityY); - - /** - * Returns true if the second panel's contents haven't been scrolled at all. This value is - * used to determine whether or not we can fully expand the header on downwards scrolls. - * - * Instead of using this callback, it would be preferable to instead fully expand the header - * on a View#onNestedFlingOver() callback. The behavior would be nicer. Unfortunately, - * no such callback exists yet (b/17547693). - */ - public boolean isScrollableChildUnscrolled(); - } - - public OverlappingPaneLayout(Context context) { - this(context, null); - } - - public OverlappingPaneLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public OverlappingPaneLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final float density = context.getResources().getDisplayMetrics().density; - mOverhangSize = (int) (DEFAULT_OVERHANG_SIZE * density + 0.5f); - - setWillNotDraw(false); - - ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate()); - - mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback()); - mDragHelper.setMinVelocity(MIN_FLING_VELOCITY * density); - - mReleaseScrollSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - } - - /** - * Set an offset, somewhere in between the panel's fully closed state and fully opened state, - * where the panel can be temporarily pinned or opened up to. - * - * @param offset Offset in pixels - */ - public void setIntermediatePinnedOffset(int offset) { - mIntermediateOffset = offset; - } - - /** - * Set the view that can be used to start dragging the sliding pane. - */ - public void setCapturableView(View capturableView) { - mCapturableView = capturableView; - } - - public void setPanelSlideCallbacks(PanelSlideCallbacks listener) { - mPanelSlideCallbacks = listener; - } - - void dispatchOnPanelSlide(View panel) { - mPanelSlideCallbacks.onPanelSlide(panel, mSlideOffset); - } - - void dispatchOnPanelOpened(View panel) { - mPanelSlideCallbacks.onPanelOpened(panel); - sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - - void dispatchOnPanelClosed(View panel) { - mPanelSlideCallbacks.onPanelClosed(panel); - sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - - void updateObscuredViewsVisibility(View panel) { - final int startBound = getPaddingTop(); - final int endBound = getHeight() - getPaddingBottom(); - - final int leftBound = getPaddingLeft(); - final int rightBound = getWidth() - getPaddingRight(); - final int left; - final int right; - final int top; - final int bottom; - if (panel != null && viewIsOpaque(panel)) { - left = panel.getLeft(); - right = panel.getRight(); - top = panel.getTop(); - bottom = panel.getBottom(); - } else { - left = right = top = bottom = 0; - } - - for (int i = 0, childCount = getChildCount(); i < childCount; i++) { - final View child = getChildAt(i); - - if (child == panel) { - // There are still more children above the panel but they won't be affected. - break; - } - - final int clampedChildLeft = Math.max(leftBound, child.getLeft()); - final int clampedChildRight = Math.min(rightBound, child.getRight()); - final int clampedChildTop = Math.max(startBound, child.getTop()); - final int clampedChildBottom = Math.min(endBound, child.getBottom()); - - final int vis; - if (clampedChildLeft >= left && clampedChildTop >= top && - clampedChildRight <= right && clampedChildBottom <= bottom) { - vis = INVISIBLE; - } else { - vis = VISIBLE; - } - child.setVisibility(vis); - } - } - - void setAllChildrenVisible() { - for (int i = 0, childCount = getChildCount(); i < childCount; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == INVISIBLE) { - child.setVisibility(VISIBLE); - } - } - } - - private static boolean viewIsOpaque(View v) { - if (ViewCompat.isOpaque(v)) return true; - - final Drawable bg = v.getBackground(); - if (bg != null) { - return bg.getOpacity() == PixelFormat.OPAQUE; - } - return false; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mFirstLayout = true; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mFirstLayout = true; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode != MeasureSpec.EXACTLY) { - if (isInEditMode()) { - // Don't crash the layout editor. Consume all of the space if specified - // or pick a magic number from thin air otherwise. - // TODO Better communication with tools of this bogus state. - // It will crash on a real device. - if (widthMode == MeasureSpec.AT_MOST) { - widthMode = MeasureSpec.EXACTLY; - } else if (widthMode == MeasureSpec.UNSPECIFIED) { - widthMode = MeasureSpec.EXACTLY; - widthSize = 300; - } - } else { - throw new IllegalStateException("Width must have an exact value or MATCH_PARENT"); - } - } else if (heightMode == MeasureSpec.UNSPECIFIED) { - if (isInEditMode()) { - // Don't crash the layout editor. Pick a magic number from thin air instead. - // TODO Better communication with tools of this bogus state. - // It will crash on a real device. - if (heightMode == MeasureSpec.UNSPECIFIED) { - heightMode = MeasureSpec.AT_MOST; - heightSize = 300; - } - } else { - throw new IllegalStateException("Height must not be UNSPECIFIED"); - } - } - - int layoutWidth = 0; - int maxLayoutWidth = -1; - switch (widthMode) { - case MeasureSpec.EXACTLY: - layoutWidth = maxLayoutWidth = widthSize - getPaddingLeft() - getPaddingRight(); - break; - case MeasureSpec.AT_MOST: - maxLayoutWidth = widthSize - getPaddingLeft() - getPaddingRight(); - break; - } - - float weightSum = 0; - boolean canSlide = false; - final int heightAvailable = heightSize - getPaddingTop() - getPaddingBottom(); - int heightRemaining = heightAvailable; - final int childCount = getChildCount(); - - if (childCount > 2) { - Log.e(TAG, "onMeasure: More than two child views are not supported."); - } - - // We'll find the current one below. - mSlideableView = null; - - // First pass. Measure based on child LayoutParams width/height. - // Weight will incur a second pass. - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (child.getVisibility() == GONE) { - continue; - } - - if (lp.weight > 0) { - weightSum += lp.weight; - - // If we have no height, weight is the only contributor to the final size. - // Measure this view on the weight pass only. - if (lp.height == 0) continue; - } - - int childHeightSpec; - final int verticalMargin = lp.topMargin + lp.bottomMargin; - if (lp.height == LayoutParams.WRAP_CONTENT) { - childHeightSpec = MeasureSpec.makeMeasureSpec(heightAvailable - verticalMargin, - MeasureSpec.AT_MOST); - } else if (lp.height == LayoutParams.MATCH_PARENT) { - childHeightSpec = MeasureSpec.makeMeasureSpec(heightAvailable - verticalMargin, - MeasureSpec.EXACTLY); - } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - } - - int childWidthSpec; - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, MeasureSpec.AT_MOST); - } else if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, MeasureSpec.EXACTLY); - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); - } - - child.measure(childWidthSpec, childHeightSpec); - final int childWidth = child.getMeasuredWidth(); - final int childHeight = child.getMeasuredHeight(); - - if (widthMode == MeasureSpec.AT_MOST && childWidth > layoutWidth) { - layoutWidth = Math.min(childWidth, maxLayoutWidth); - } - - heightRemaining -= childHeight; - canSlide |= lp.slideable = heightRemaining < 0; - if (lp.slideable) { - mSlideableView = child; - } - } - - // Resolve weight and make sure non-sliding panels are smaller than the full screen. - if (canSlide || weightSum > 0) { - final int fixedPanelHeightLimit = heightAvailable - mOverhangSize; - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - - if (child.getVisibility() == GONE) { - continue; - } - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (child.getVisibility() == GONE) { - continue; - } - - final boolean skippedFirstPass = lp.height == 0 && lp.weight > 0; - final int measuredHeight = skippedFirstPass ? 0 : child.getMeasuredHeight(); - if (canSlide && child != mSlideableView) { - if (lp.height < 0 && (measuredHeight > fixedPanelHeightLimit || lp.weight > 0)) { - // Fixed panels in a sliding configuration should - // be clamped to the fixed panel limit. - final int childWidthSpec; - if (skippedFirstPass) { - // Do initial width measurement if we skipped measuring this view - // the first time around. - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, - MeasureSpec.AT_MOST); - } else if (lp.height == LayoutParams.MATCH_PARENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, - MeasureSpec.EXACTLY); - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, - MeasureSpec.EXACTLY); - } - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec( - child.getMeasuredWidth(), MeasureSpec.EXACTLY); - } - final int childHeightSpec = MeasureSpec.makeMeasureSpec( - fixedPanelHeightLimit, MeasureSpec.EXACTLY); - child.measure(childWidthSpec, childHeightSpec); - } - } else if (lp.weight > 0) { - int childWidthSpec; - if (lp.height == 0) { - // This was skipped the first time; figure out a real width spec. - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, - MeasureSpec.AT_MOST); - } else if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, - MeasureSpec.EXACTLY); - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, - MeasureSpec.EXACTLY); - } - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec( - child.getMeasuredWidth(), MeasureSpec.EXACTLY); - } - - if (canSlide) { - // Consume available space - final int verticalMargin = lp.topMargin + lp.bottomMargin; - final int newHeight = heightAvailable - verticalMargin; - final int childHeightSpec = MeasureSpec.makeMeasureSpec( - newHeight, MeasureSpec.EXACTLY); - if (measuredHeight != newHeight) { - child.measure(childWidthSpec, childHeightSpec); - } - } else { - // Distribute the extra width proportionally similar to LinearLayout - final int heightToDistribute = Math.max(0, heightRemaining); - final int addedHeight = (int) (lp.weight * heightToDistribute / weightSum); - final int childHeightSpec = MeasureSpec.makeMeasureSpec( - measuredHeight + addedHeight, MeasureSpec.EXACTLY); - child.measure(childWidthSpec, childHeightSpec); - } - } - } - } - - final int measuredHeight = heightSize; - final int measuredWidth = layoutWidth + getPaddingLeft() + getPaddingRight(); - - setMeasuredDimension(measuredWidth, measuredHeight); - mCanSlide = canSlide; - - if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE && !canSlide) { - // Cancel scrolling in progress, it's no longer relevant. - mDragHelper.abort(); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP); - - final int height = b - t; - final int paddingTop = getPaddingTop(); - final int paddingBottom = getPaddingBottom(); - final int paddingLeft = getPaddingLeft(); - - final int childCount = getChildCount(); - int yStart = paddingTop; - int nextYStart = yStart; - - if (mFirstLayout) { - mSlideOffset = mCanSlide && mPreservedOpenState ? 1.f : 0.f; - } - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - - if (child.getVisibility() == GONE) { - continue; - } - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - final int childHeight = child.getMeasuredHeight(); - - if (lp.slideable) { - final int margin = lp.topMargin + lp.bottomMargin; - final int range = Math.min(nextYStart, - height - paddingBottom - mOverhangSize) - yStart - margin; - mSlideRange = range; - final int lpMargin = lp.topMargin; - final int pos = (int) (range * mSlideOffset); - yStart += pos + lpMargin; - updateSlideOffset(pos); - } else { - yStart = nextYStart; - } - - final int childTop = yStart; - final int childBottom = childTop + childHeight; - final int childLeft = paddingLeft; - final int childRight = childLeft + child.getMeasuredWidth(); - - child.layout(childLeft, childTop, childRight, childBottom); - - nextYStart += child.getHeight(); - } - - if (mFirstLayout) { - updateObscuredViewsVisibility(mSlideableView); - } - - mFirstLayout = false; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - // Recalculate sliding panes and their details - if (h != oldh) { - mFirstLayout = true; - } - } - - @Override - public void requestChildFocus(View child, View focused) { - super.requestChildFocus(child, focused); - if (!isInTouchMode() && !mCanSlide) { - mPreservedOpenState = child == mSlideableView; - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - final int action = MotionEventCompat.getActionMasked(ev); - - // Preserve the open state based on the last view that was touched. - if (!mCanSlide && action == MotionEvent.ACTION_DOWN && getChildCount() > 1) { - // After the first things will be slideable. - final View secondChild = getChildAt(1); - if (secondChild != null) { - mPreservedOpenState = !mDragHelper.isViewUnder(secondChild, - (int) ev.getX(), (int) ev.getY()); - } - } - - if (!mCanSlide || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) { - if (!mIsInNestedScroll) { - mDragHelper.cancel(); - } - return super.onInterceptTouchEvent(ev); - } - - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - if (!mIsInNestedScroll) { - mDragHelper.cancel(); - } - return false; - } - - switch (action) { - case MotionEvent.ACTION_DOWN: { - mIsUnableToDrag = false; - final float x = ev.getX(); - final float y = ev.getY(); - mInitialMotionX = x; - mInitialMotionY = y; - - break; - } - - case MotionEvent.ACTION_MOVE: { - final float x = ev.getX(); - final float y = ev.getY(); - final float adx = Math.abs(x - mInitialMotionX); - final float ady = Math.abs(y - mInitialMotionY); - final int slop = mDragHelper.getTouchSlop(); - if (ady > slop && adx > ady || !isCapturableViewUnder((int) x, (int) y)) { - if (!mIsInNestedScroll) { - mDragHelper.cancel(); - } - mIsUnableToDrag = true; - return false; - } - } - } - - final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev); - - return interceptForDrag; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (!mCanSlide) { - return super.onTouchEvent(ev); - } - - mDragHelper.processTouchEvent(ev); - - final int action = ev.getAction(); - boolean wantTouchEvents = true; - - switch (action & MotionEventCompat.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { - final float x = ev.getX(); - final float y = ev.getY(); - mInitialMotionX = x; - mInitialMotionY = y; - break; - } - } - - return wantTouchEvents; - } - - /** - * Refreshes the {@link OverlappingPaneLayout} be attempting to re-open or re-close the pane. - * This ensures that the overlapping pane is repositioned based on any changes to the view - * which is being overlapped. - *

- * The {@link #openPane()} and {@link #closePane()} methods do not perform any animation if the - * pane has already been positioned appropriately. - */ - public void refresh() { - if (isOpen()) { - openPane(); - } else { - closePane(); - } - } - - private boolean closePane(View pane, int initialVelocity) { - if (mFirstLayout || smoothSlideTo(0.f, initialVelocity)) { - mPreservedOpenState = false; - return true; - } - return false; - } - - private boolean openPane(View pane, int initialVelocity) { - if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) { - mPreservedOpenState = true; - return true; - } - return false; - } - - private void updateSlideOffset(int offsetPx) { - mSlideOffsetPx = offsetPx; - mSlideOffset = (float) mSlideOffsetPx / mSlideRange; - } - - /** - * Open the sliding pane if it is currently slideable. If first layout - * has already completed this will animate. - * - * @return true if the pane was slideable and is now open/in the process of opening - */ - public boolean openPane() { - return openPane(mSlideableView, 0); - } - - /** - * Close the sliding pane if it is currently slideable. If first layout - * has already completed this will animate. - * - * @return true if the pane was slideable and is now closed/in the process of closing - */ - public boolean closePane() { - return closePane(mSlideableView, 0); - } - - /** - * Check if the layout is open. It can be open either because the slider - * itself is open revealing the left pane, or if all content fits without sliding. - * - * @return true if sliding panels are open - */ - public boolean isOpen() { - return !mCanSlide || mSlideOffset > 0; - } - - /** - * Check if the content in this layout cannot fully fit side by side and therefore - * the content pane can be slid back and forth. - * - * @return true if content in this layout can be slid open and closed - */ - public boolean isSlideable() { - return mCanSlide; - } - - private void onPanelDragged(int newTop) { - if (mSlideableView == null) { - // This can happen if we're aborting motion during layout because everything now fits. - mSlideOffset = 0; - return; - } - final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams(); - - final int lpMargin = lp.topMargin; - final int topBound = getPaddingTop() + lpMargin; - - updateSlideOffset(newTop - topBound); - - dispatchOnPanelSlide(mSlideableView); - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - boolean result; - final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); - - if (mCanSlide && !lp.slideable && mSlideableView != null) { - // Clip against the slider; no sense drawing what will immediately be covered. - canvas.getClipBounds(mTmpRect); - - mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop()); - canvas.clipRect(mTmpRect); - } - - if (Build.VERSION.SDK_INT >= 11) { // HC - result = super.drawChild(canvas, child, drawingTime); - } else { - if (child.isDrawingCacheEnabled()) { - child.setDrawingCacheEnabled(false); - } - result = super.drawChild(canvas, child, drawingTime); - } - - canvas.restoreToCount(save); - - return result; - } - - /** - * Smoothly animate mDraggingPane to the target X position within its range. - * - * @param slideOffset position to animate to - * @param velocity initial velocity in case of fling, or 0. - */ - boolean smoothSlideTo(float slideOffset, int velocity) { - if (!mCanSlide) { - // Nothing to do. - return false; - } - - final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams(); - - int y; - int topBound = getPaddingTop() + lp.topMargin; - y = (int) (topBound + slideOffset * mSlideRange); - - if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) { - setAllChildrenVisible(); - ViewCompat.postInvalidateOnAnimation(this); - return true; - } - return false; - } - - @Override - public void computeScroll() { - if (mDragHelper.continueSettling(/* deferCallbacks = */ false)) { - if (!mCanSlide) { - mDragHelper.abort(); - return; - } - - ViewCompat.postInvalidateOnAnimation(this); - } - } - - private boolean isCapturableViewUnder(int x, int y) { - View capturableView = mCapturableView != null ? mCapturableView : mSlideableView; - if (capturableView == null) { - return false; - } - int[] viewLocation = new int[2]; - capturableView.getLocationOnScreen(viewLocation); - int[] parentLocation = new int[2]; - this.getLocationOnScreen(parentLocation); - int screenX = parentLocation[0] + x; - int screenY = parentLocation[1] + y; - return screenX >= viewLocation[0] - && screenX < viewLocation[0] + capturableView.getWidth() - && screenY >= viewLocation[1] - && screenY < viewLocation[1] + capturableView.getHeight(); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof MarginLayoutParams - ? new LayoutParams((MarginLayoutParams) p) - : new LayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams && super.checkLayoutParams(p); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - - SavedState ss = new SavedState(superState); - ss.isOpen = isSlideable() ? isOpen() : mPreservedOpenState; - - return ss; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - - if (ss.isOpen) { - openPane(); - } else { - closePane(); - } - mPreservedOpenState = ss.isOpen; - } - - @Override - public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { - final boolean startNestedScroll = (nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0; - if (startNestedScroll) { - mIsInNestedScroll = true; - mDragHelper.startNestedScroll(mSlideableView); - } - if (DEBUG) { - Log.d(TAG, "onStartNestedScroll: " + startNestedScroll); - } - return startNestedScroll; - } - - @Override - public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { - if (dy == 0) { - // Nothing to do - return; - } - if (DEBUG) { - Log.d(TAG, "onNestedPreScroll: " + dy); - } - - mInNestedPreScrollDownwards = dy < 0; - mInNestedPreScrollUpwards = dy > 0; - mIsInNestedFling = false; - mDragHelper.processNestedScroll(mSlideableView, 0, -dy, consumed); - } - - @Override - public boolean onNestedPreFling(View target, float velocityX, float velocityY) { - if (!(velocityY > 0 && mSlideOffsetPx != 0 - || velocityY < 0 && mSlideOffsetPx < mIntermediateOffset - || velocityY < 0 && mSlideOffsetPx < mSlideRange - && mPanelSlideCallbacks.isScrollableChildUnscrolled())) { - // No need to consume the fling if the fling won't collapse or expand the header. - // How far we are willing to expand the header depends on isScrollableChildUnscrolled(). - return false; - } - - if (DEBUG) { - Log.d(TAG, "onNestedPreFling: " + velocityY); - } - mInUpwardsPreFling = velocityY > 0; - mIsInNestedFling = true; - mIsInNestedScroll = false; - mDragHelper.processNestedFling(mSlideableView, (int) -velocityY); - return true; - } - - @Override - public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, - int dyUnconsumed) { - if (DEBUG) { - Log.d(TAG, "onNestedScroll: " + dyUnconsumed); - } - mIsInNestedFling = false; - mDragHelper.processNestedScroll(mSlideableView, 0, -dyUnconsumed, null); - } - - @Override - public void onStopNestedScroll(View child) { - if (DEBUG) { - Log.d(TAG, "onStopNestedScroll"); - } - if (mIsInNestedScroll && !mIsInNestedFling) { - mDragHelper.stopNestedScroll(mSlideableView); - mInNestedPreScrollDownwards = false; - mInNestedPreScrollUpwards = false; - mIsInNestedScroll = false; - } - } - - private class DragHelperCallback extends ViewDragHelper.Callback { - - @Override - public boolean tryCaptureView(View child, int pointerId) { - if (mIsUnableToDrag) { - return false; - } - - return ((LayoutParams) child.getLayoutParams()).slideable; - } - - @Override - public void onViewDragStateChanged(int state) { - if (DEBUG) { - Log.d(TAG, "onViewDragStateChanged: " + state); - } - - if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) { - if (mSlideOffset == 0) { - updateObscuredViewsVisibility(mSlideableView); - dispatchOnPanelClosed(mSlideableView); - mPreservedOpenState = false; - } else { - dispatchOnPanelOpened(mSlideableView); - mPreservedOpenState = true; - } - } - - if (state == ViewDragHelper.STATE_IDLE - && mDragHelper.getVelocityMagnitude() > 0 - && mIsInNestedFling) { - mIsInNestedFling = false; - final int flingVelocity = !mInUpwardsPreFling ? - -mDragHelper.getVelocityMagnitude() : mDragHelper.getVelocityMagnitude(); - mPanelSlideCallbacks.onPanelFlingReachesEdge(flingVelocity); - } - } - - @Override - public void onViewCaptured(View capturedChild, int activePointerId) { - // Make all child views visible in preparation for sliding things around - setAllChildrenVisible(); - } - - @Override - public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - onPanelDragged(top); - invalidate(); - } - - @Override - public void onViewFling(View releasedChild, float xVelocity, float yVelocity) { - if (releasedChild == null) { - return; - } - if (DEBUG) { - Log.d(TAG, "onViewFling: " + yVelocity); - } - - // Flings won't always fully expand or collapse the header. Instead of performing the - // fling and then waiting for the fling to end before snapping into place, we - // immediately snap into place if we predict the fling won't fully expand or collapse - // the header. - int yOffsetPx = mDragHelper.predictFlingYOffset((int) yVelocity); - if (yVelocity < 0) { - // Only perform a fling if we know the fling will fully compress the header. - if (-yOffsetPx > mSlideOffsetPx) { - mDragHelper.flingCapturedView(releasedChild.getLeft(), /* minTop = */ 0, - mSlideRange, Integer.MAX_VALUE, (int) yVelocity); - } else { - mIsInNestedFling = false; - onViewReleased(releasedChild, xVelocity, yVelocity); - } - } else { - // Only perform a fling if we know the fling will expand the header as far - // as it can possible be expanded, given the isScrollableChildUnscrolled() value. - if (yOffsetPx + mSlideOffsetPx >= mSlideRange - && mPanelSlideCallbacks.isScrollableChildUnscrolled()) { - mDragHelper.flingCapturedView(releasedChild.getLeft(), /* minTop = */ 0, - Integer.MAX_VALUE, mSlideRange, (int) yVelocity); - } else if (yOffsetPx + mSlideOffsetPx >= mIntermediateOffset - && mSlideOffsetPx <= mIntermediateOffset - && !mPanelSlideCallbacks.isScrollableChildUnscrolled()) { - mDragHelper.flingCapturedView(releasedChild.getLeft(), /* minTop = */ 0, - Integer.MAX_VALUE, mIntermediateOffset, (int) yVelocity); - } else { - mIsInNestedFling = false; - onViewReleased(releasedChild, xVelocity, yVelocity); - } - } - - mInNestedPreScrollDownwards = false; - mInNestedPreScrollUpwards = false; - - // Without this invalidate, some calls to flingCapturedView can have no affect. - invalidate(); - } - - @Override - public void onViewReleased(View releasedChild, float xvel, float yvel) { - if (DEBUG) { - Log.d(TAG, "onViewReleased: " - + " mIsInNestedFling=" + mIsInNestedFling - + " unscrolled=" + mPanelSlideCallbacks.isScrollableChildUnscrolled() - + ", mInNestedPreScrollDownwards = " + mInNestedPreScrollDownwards - + ", mInNestedPreScrollUpwards = " + mInNestedPreScrollUpwards - + ", yvel=" + yvel); - } - if (releasedChild == null) { - return; - } - - final LayoutParams lp = (LayoutParams) releasedChild.getLayoutParams(); - int top = getPaddingTop() + lp.topMargin; - - // Decide where to snap to according to the current direction of motion and the current - // position. The velocity's magnitude has no bearing on this. - if (mInNestedPreScrollDownwards || yvel > 0) { - // Scrolling downwards - if (mSlideOffsetPx > mIntermediateOffset + mReleaseScrollSlop) { - top += mSlideRange; - } else if (mSlideOffsetPx > mReleaseScrollSlop) { - top += mIntermediateOffset; - } else { - // Offset is very close to 0 - } - } else if (mInNestedPreScrollUpwards || yvel < 0) { - // Scrolling upwards - if (mSlideOffsetPx > mSlideRange - mReleaseScrollSlop) { - // Offset is very close to mSlideRange - top += mSlideRange; - } else if (mSlideOffsetPx > mIntermediateOffset - mReleaseScrollSlop) { - // Offset is between mIntermediateOffset and mSlideRange. - top += mIntermediateOffset; - } else { - // Offset is between 0 and mIntermediateOffset. - } - } else { - // Not moving upwards or downwards. This case can only be triggered when directly - // dragging the tabs. We don't bother to remember previous scroll direction - // when directly dragging the tabs. - if (0 <= mSlideOffsetPx && mSlideOffsetPx <= mIntermediateOffset / 2) { - // Offset is between 0 and mIntermediateOffset, but closer to 0 - // Leave top unchanged - } else if (mIntermediateOffset / 2 <= mSlideOffsetPx - && mSlideOffsetPx <= (mIntermediateOffset + mSlideRange) / 2) { - // Offset is closest to mIntermediateOffset - top += mIntermediateOffset; - } else { - // Offset is between mIntermediateOffset and mSlideRange, but closer to - // mSlideRange - top += mSlideRange; - } - } - - mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top); - invalidate(); - } - - @Override - public int getViewVerticalDragRange(View child) { - return mSlideRange; - } - - @Override - public int clampViewPositionHorizontal(View child, int left, int dx) { - // Make sure we never move views horizontally. - return child.getLeft(); - } - - @Override - public int clampViewPositionVertical(View child, int top, int dy) { - final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams(); - - final int newTop; - int previousTop = top - dy; - int topBound = getPaddingTop() + lp.topMargin; - int bottomBound = topBound + (mPanelSlideCallbacks.isScrollableChildUnscrolled() - || !mIsInNestedScroll ? mSlideRange : mIntermediateOffset); - if (previousTop > bottomBound) { - // We were previously below the bottomBound, so loosen the bottomBound so that this - // makes sense. This can occur after the view was directly dragged by the tabs. - bottomBound = Math.max(bottomBound, mSlideRange); - } - newTop = Math.min(Math.max(top, topBound), bottomBound); - - return newTop; - } - - @Override - public void onEdgeDragStarted(int edgeFlags, int pointerId) { - mDragHelper.captureChildView(mSlideableView, pointerId); - } - } - - public static class LayoutParams extends ViewGroup.MarginLayoutParams { - private static final int[] ATTRS = new int[] { - android.R.attr.layout_weight - }; - - /** - * The weighted proportion of how much of the leftover space - * this child should consume after measurement. - */ - public float weight = 0; - - /** - * True if this pane is the slideable pane in the layout. - */ - boolean slideable; - - public LayoutParams() { - super(FILL_PARENT, FILL_PARENT); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(android.view.ViewGroup.LayoutParams source) { - super(source); - } - - public LayoutParams(MarginLayoutParams source) { - super(source); - } - - public LayoutParams(LayoutParams source) { - super(source); - this.weight = source.weight; - } - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - - final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS); - this.weight = a.getFloat(0, 0); - a.recycle(); - } - - } - - static class SavedState extends BaseSavedState { - boolean isOpen; - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - isOpen = in.readInt() != 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(isOpen ? 1 : 0); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - class AccessibilityDelegate extends AccessibilityDelegateCompat { - private final Rect mTmpRect = new Rect(); - - @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { - final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info); - super.onInitializeAccessibilityNodeInfo(host, superNode); - copyNodeInfoNoChildren(info, superNode); - superNode.recycle(); - - info.setClassName(OverlappingPaneLayout.class.getName()); - info.setSource(host); - - final ViewParent parent = ViewCompat.getParentForAccessibility(host); - if (parent instanceof View) { - info.setParent((View) parent); - } - - // This is a best-approximation of addChildrenForAccessibility() - // that accounts for filtering. - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == View.VISIBLE) { - // Force importance to "yes" since we can't read the value. - ViewCompat.setImportantForAccessibility( - child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); - info.addChild(child); - } - } - } - - @Override - public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(host, event); - - event.setClassName(OverlappingPaneLayout.class.getName()); - } - - /** - * This should really be in AccessibilityNodeInfoCompat, but there unfortunately - * seem to be a few elements that are not easily cloneable using the underlying API. - * Leave it private here as it's not general-purpose useful. - */ - private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest, - AccessibilityNodeInfoCompat src) { - final Rect rect = mTmpRect; - - src.getBoundsInParent(rect); - dest.setBoundsInParent(rect); - - src.getBoundsInScreen(rect); - dest.setBoundsInScreen(rect); - - dest.setVisibleToUser(src.isVisibleToUser()); - dest.setPackageName(src.getPackageName()); - dest.setClassName(src.getClassName()); - dest.setContentDescription(src.getContentDescription()); - - dest.setEnabled(src.isEnabled()); - dest.setClickable(src.isClickable()); - dest.setFocusable(src.isFocusable()); - dest.setFocused(src.isFocused()); - dest.setAccessibilityFocused(src.isAccessibilityFocused()); - dest.setSelected(src.isSelected()); - dest.setLongClickable(src.isLongClickable()); - - dest.addAction(src.getActions()); - - dest.setMovementGranularities(src.getMovementGranularities()); - } - } -} -- cgit v1.2.3