Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 138c58a9 authored by Chet Haase's avatar Chet Haase Committed by Android (Google) Code Review
Browse files

Merge "Add overlays to views" into jb-mr2-dev

parents b8c5ce29 91cedf1c
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -24953,6 +24953,14 @@ package android.view {
    field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
  }
  public abstract interface Overlay {
    method public abstract void add(android.graphics.drawable.Drawable);
    method public abstract void add(android.view.View);
    method public abstract void clear();
    method public abstract void remove(android.graphics.drawable.Drawable);
    method public abstract void remove(android.view.View);
  }
  public class ScaleGestureDetector {
    ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
    method public float getCurrentSpan();
@@ -25257,6 +25265,7 @@ package android.view {
    method public int getNextFocusUpId();
    method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
    method public int getOverScrollMode();
    method public android.view.Overlay getOverlay();
    method public int getPaddingBottom();
    method public int getPaddingEnd();
    method public int getPaddingLeft();
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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 android.view;

import android.graphics.drawable.Drawable;

/**
 * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
 * all other content in that view (including children, if the view is a ViewGroup). Interaction
 * with the overlay layer is done in terms of adding/removing views and drawables. Invalidation and
 * redrawing of the overlay layer (and its host view) is handled differently for views versus
 * drawables in the overlay. Views invalidate themselves as usual, causing appropriate redrawing
 * to occur automatically. Drawables, on the other hand, do not manage invalidation, so changes to
 * drawable objects should be accompanied by appropriate calls to invalidate() on the host view.
 *
 * @see android.view.View#getOverlay()
 */
public interface Overlay {

    /**
     * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
     * the host view. Any drawable added to the overlay should be removed when it is no longer
     * needed or no longer visible. There is no automatic invalidation of the host view; changes to
     * the drawable should be accompanied by appropriate invalidation calls to the host view
     * to cause the proper area of the view, and the overlay, to be redrawn.
     *
     * @param drawable The Drawable to be added to the overlay. This drawable will be
     * drawn when the view redraws its overlay.
     * @see #remove(android.graphics.drawable.Drawable)
     * @see #add(View)
     */
    void add(Drawable drawable);

    /**
     * Removes the specified Drawable from the overlay.
     *
     * @param drawable The Drawable to be removed from the overlay.
     * @see #add(android.graphics.drawable.Drawable)
     */
    void remove(Drawable drawable);

    /**
     * Adds a View to the overlay. The bounds of the added view should be relative to
     * the host view. Any view added to the overlay should be removed when it is no longer
     * needed or no longer visible. The view must not be parented elsewhere when it is added
     * to the overlay.
     *
     * @param view The View to be added to the overlay. The added view will be
     * drawn when the overlay is drawn.
     * @see #remove(View)
     * @see #add(android.graphics.drawable.Drawable)
     */
    void add(View view);

    /**
     * Removes the specified View from the overlay.
     *
     * @param view The View to be removed from the overlay.
     * @see #add(View)
     */
    void remove(View view);

    /**
     * Removes all views and drawables from the overlay.
     */
    void clear();
}
+66 −5
Original line number Diff line number Diff line
@@ -3222,6 +3222,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    AccessibilityDelegate mAccessibilityDelegate;
    /**
     * The view's overlay layer. Developers get a reference to the overlay via getOverlay()
     * and add/remove objects to/from the overlay directly through the Overlay methods.
     */
    ViewOverlay mOverlay;
    /**
     * Consistency verifier for debugging purposes.
     * @hide
@@ -9590,7 +9596,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                mDisplayList.setTop(mTop);
            }
            onSizeChanged(width, mBottom - mTop, width, oldHeight);
            sizeChange(width, mBottom - mTop, width, oldHeight);
            if (!matrixIsIdentity) {
                if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9663,7 +9669,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                mDisplayList.setBottom(mBottom);
            }
            onSizeChanged(width, mBottom - mTop, width, oldHeight);
            sizeChange(width, mBottom - mTop, width, oldHeight);
            if (!matrixIsIdentity) {
                if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9730,7 +9736,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                mDisplayList.setLeft(left);
            }
            onSizeChanged(mRight - mLeft, height, oldWidth, height);
            sizeChange(mRight - mLeft, height, oldWidth, height);
            if (!matrixIsIdentity) {
                if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9794,7 +9800,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                mDisplayList.setRight(mRight);
            }
            onSizeChanged(mRight - mLeft, height, oldWidth, height);
            sizeChange(mRight - mLeft, height, oldWidth, height);
            if (!matrixIsIdentity) {
                if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -12092,6 +12098,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        //System.out.println("Attached! " + this);
        mAttachInfo = info;
        if (mOverlay != null) {
            mOverlay.mAttachInfo = info;
        }
        mWindowAttachCount++;
        // We will need to evaluate the drawable state at least once.
        mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
@@ -12160,6 +12169,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        mAttachInfo = null;
        if (mOverlay != null) {
            mOverlay.mAttachInfo = null;
        }
    }
    /**
@@ -12815,6 +12827,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        dispatchDraw(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.draw(canvas);
                        }
                    } else {
                        draw(canvas);
                    }
@@ -13128,6 +13143,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                dispatchDraw(canvas);
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.draw(canvas);
                }
            } else {
                draw(canvas);
            }
@@ -13883,6 +13901,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            // Step 6, draw decorations (scrollbars)
            onDrawScrollBars(canvas);
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.dispatchDraw(canvas);
            }
            // we're done...
            return;
        }
@@ -14022,6 +14044,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.dispatchDraw(canvas);
        }
    }
    /**
     * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary
     */
    private void setupOverlay() {
        if (mOverlay == null) {
            mOverlay = new ViewOverlay(mContext, this);
            mOverlay.mAttachInfo = mAttachInfo;
            mOverlay.setRight(mRight);
            mOverlay.setBottom(mBottom);
        }
    }
    /**
     * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables
     * and/or views to the overlay will cause them to be displayed whenever the view itself is
     * redrawn. Objects in the overlay should be actively managed: remove them when they should
     * not be displayed anymore and invalidate this view appropriately when overlay drawables
     * change. The overlay will always have the same size as its host view.
     *
     * @return The Overlay object for this view.
     * @see Overlay
     */
    public Overlay getOverlay() {
        setupOverlay();
        return mOverlay;
    }
    /**
@@ -14277,7 +14330,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                        mTransformationInfo.mMatrixDirty = true;
                    }
                }
                onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
                sizeChange(newWidth, newHeight, oldWidth, oldHeight);
            }
            if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
@@ -14301,6 +14354,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return changed;
    }
    private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
        onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
        if (mOverlay != null) {
            mOverlay.setRight(mRight);
            mOverlay.setBottom(mBottom);
        }
    }
    /**
     * Finalize inflating a view from XML.  This is called as the last phase
     * of inflation, after all child views have been added.
+29 −1
Original line number Diff line number Diff line
@@ -1869,7 +1869,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if (childrenCount != 0) {
                    if (childrenCount != 0 || mOverlay != null) {
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final View[] children = mChildren;
@@ -1906,6 +1906,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                                break;
                            }
                        }
                        if (mOverlay != null && newTouchTarget == null) {
                            // Check to see whether the overlay can handle the event
                            final View child = mOverlay;
                            if (canViewReceivePointerEvents(child) &&
                                    isTransformedTouchPointInView(x, y, child, null)) {
                                newTouchTarget = getTouchTarget(child);
                                if (newTouchTarget != null) {
                                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                                } else {
                                    resetCancelNextUpFlag(child);
                                    if (dispatchTransformedTouchEvent(ev, false, child,
                                            idBitsToAssign)) {
                                        mLastTouchDownTime = ev.getDownTime();
                                        mLastTouchDownX = ev.getX();
                                        mLastTouchDownY = ev.getY();
                                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                        alreadyDispatchedToNewTouchTarget = true;
                                    }
                                }
                            }
                        }
                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -3022,6 +3043,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                child.mRecreateDisplayList = false;
            }
        }
        if (mOverlay != null) {
            mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED)
                    == PFLAG_INVALIDATED;
            mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED;
            mOverlay.getDisplayList();
            mOverlay.mRecreateDisplayList = false;
        }
    }

    /**
+203 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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 android.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;

import java.util.ArrayList;

/**
 * ViewOverlay is a container that View uses to host all objects (views and drawables) that
 * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
 * added to the overlay via the add/remove methods in this class. These views and drawables are
 * then drawn whenever the view itself is drawn, after which it will draw its overlay (if it
 * exists).
 *
 * Besides managing and drawing the list of drawables, this class serves two purposes:
 * (1) it noops layout calls because children are absolutely positioned and
 * (2) it forwards all invalidation calls to its host view. The invalidation redirect is
 * necessary because the overlay is not a child of the host view and invalidation cannot
 * therefore follow the normal path up through the parent hierarchy.
 *
 * @hide
 */
class ViewOverlay extends ViewGroup implements Overlay {

    /**
     * The View for which this is an overlay. Invalidations of the overlay are redirected to
     * this host view.
     */
    View mHostView;

    /**
     * The set of drawables to draw when the overlay is rendered.
     */
    ArrayList<Drawable> mDrawables = null;

    ViewOverlay(Context context, View host) {
        super(context);
        mHostView = host;
        mParent = mHostView.getParent();
    }

    @Override
    public void add(Drawable drawable) {
        if (mDrawables == null) {
            mDrawables = new ArrayList<Drawable>();
        }
        if (!mDrawables.contains(drawable)) {
            // Make each drawable unique in the overlay; can't add it more than once
            mDrawables.add(drawable);
            invalidate(drawable.getBounds());
        }
    }

    @Override
    public void remove(Drawable drawable) {
        if (mDrawables != null) {
            mDrawables.remove(drawable);
            invalidate(drawable.getBounds());
        }
    }

    @Override
    public void add(View child) {
        super.addView(child);
    }

    @Override
    public void remove(View view) {
        super.removeView(view);
    }

    @Override
    public void clear() {
        removeAllViews();
        mDrawables.clear();
    }

    boolean isEmpty() {
        if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) {
            return true;
        }
        return false;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
        for (int i = 0; i < numDrawables; ++i) {
            mDrawables.get(i).draw(canvas);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // Noop: children are positioned absolutely
    }

    /*
     The following invalidation overrides exist for the purpose of redirecting invalidation to
     the host view. The overlay is not parented to the host view (since a View cannot be a parent),
     so the invalidation cannot proceed through the normal parent hierarchy.
     There is a built-in assumption that the overlay exactly covers the host view, therefore
     the invalidation rectangles received do not need to be adjusted when forwarded to
     the host view.
     */

    @Override
    public void invalidate(Rect dirty) {
        super.invalidate(dirty);
        if (mHostView != null) {
            dirty.offset(getLeft(), getTop());
            mHostView.invalidate(dirty);
        }
    }

    @Override
    public void invalidate(int l, int t, int r, int b) {
        super.invalidate(l, t, r, b);
        if (mHostView != null) {
            mHostView.invalidate(l, t, r, b);
        }
    }

    @Override
    public void invalidate() {
        super.invalidate();
        if (mHostView != null) {
            mHostView.invalidate();
        }
    }

    @Override
    void invalidate(boolean invalidateCache) {
        super.invalidate(invalidateCache);
        if (mHostView != null) {
            mHostView.invalidate(invalidateCache);
        }
    }

    @Override
    void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
        super.invalidateViewProperty(invalidateParent, forceRedraw);
        if (mHostView != null) {
            mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
        }
    }

    @Override
    protected void invalidateParentCaches() {
        super.invalidateParentCaches();
        if (mHostView != null) {
            mHostView.invalidateParentCaches();
        }
    }

    @Override
    protected void invalidateParentIfNeeded() {
        super.invalidateParentIfNeeded();
        if (mHostView != null) {
            mHostView.invalidateParentIfNeeded();
        }
    }

    public void invalidateChildFast(View child, final Rect dirty) {
        if (mHostView != null) {
            // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL
            // properties and a dirty rect instead of causing a real invalidation of the host view
            int left = child.mLeft;
            int top = child.mTop;
            if (!child.getMatrix().isIdentity()) {
                child.transformRect(dirty);
            }
            dirty.offset(left, top);
            mHostView.invalidate(dirty);
        }
    }

    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        if (mHostView != null) {
            mHostView.invalidate(dirty);
        }
        return null;
    }
}