summaryrefslogtreecommitdiff
path: root/android/view/ViewGroup_Delegate.java
blob: 4b760a7d46389a666c7d47dc3dc693d7d0f39e14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Copyright (C) 2014 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 com.android.resources.Density;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;

import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Path_Delegate;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.view.animation.Transformation;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

/**
 * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
 * <p/>
 * Through the layoutlib_create tool, the original  methods of ViewGroup have been replaced by calls
 * to methods of the same name in this delegate class.
 */
public class ViewGroup_Delegate {

    /**
     * Overrides the original drawChild call in ViewGroup to draw the shadow.
     */
    @LayoutlibDelegate
    /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
            long drawingTime) {
        if (child.getZ() > thisVG.getZ()) {
            // The background's bounds are set lazily. Make sure they are set correctly so that
            // the outline obtained is correct.
            child.setBackgroundBounds();
            ViewOutlineProvider outlineProvider = child.getOutlineProvider();
            if (outlineProvider != null) {
                Outline outline = child.mAttachInfo.mTmpOutline;
                outlineProvider.getOutline(child, outline);
                if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
                    int restoreTo = transformCanvas(thisVG, canvas, child);
                    drawShadow(thisVG, canvas, child, outline);
                    canvas.restoreToCount(restoreTo);
                }
            }
        }
        return thisVG.drawChild_Original(canvas, child, drawingTime);
    }

    private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
            Outline outline) {
        float elevation = getElevation(child, parent);
        if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
            RectShadowPainter.paintShadow(outline, elevation, canvas);
            return;
        }
        BufferedImage shadow = null;
        if (outline.mPath != null) {
            shadow = getPathShadow(outline, canvas, elevation);
        }
        if (shadow == null) {
            return;
        }
        Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
                Density.getEnum(canvas.getDensity()));
        Rect clipBounds = canvas.getClipBounds();
        Rect newBounds = new Rect(clipBounds);
        newBounds.inset((int)-elevation, (int)-elevation);
        canvas.clipRect(newBounds, Op.REPLACE);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.clipRect(clipBounds, Op.REPLACE);
    }

    private static float getElevation(View child, ViewGroup parent) {
        return child.getZ() - parent.getZ();
    }

    private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation) {
        Rect clipBounds = canvas.getClipBounds();
        if (clipBounds.isEmpty()) {
          return null;
        }
        BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = image.createGraphics();
        graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
        graphics.dispose();
        return ShadowPainter.createDropShadow(image, (int) elevation);
    }

    // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
    // which were never taken. Ideally, we should hook up the shadow code in the same method so
    // that we don't have to transform the canvas twice.
    private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
        final int restoreTo = canvas.save();
        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
        int flags = thisVG.mGroupFlags;
        Transformation transformToApply = null;
        boolean concatMatrix = false;
        if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
            final Transformation t = thisVG.getChildTransformation();
            final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
            if (hasTransform) {
                final int transformType = t.getTransformationType();
                transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
            }
        }
        concatMatrix |= childHasIdentityMatrix;

        child.computeScroll();
        int sx = child.mScrollX;
        int sy = child.mScrollY;

        canvas.translate(child.mLeft - sx, child.mTop - sy);
        float alpha = child.getAlpha() * child.getTransitionAlpha();

        if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
            if (transformToApply != null || !childHasIdentityMatrix) {
                int transX = -sx;
                int transY = -sy;

                if (transformToApply != null) {
                    if (concatMatrix) {
                        // Undo the scroll translation, apply the transformation matrix,
                        // then redo the scroll translate to get the correct result.
                        canvas.translate(-transX, -transY);
                        canvas.concat(transformToApply.getMatrix());
                        canvas.translate(transX, transY);
                    }
                    if (!childHasIdentityMatrix) {
                        canvas.translate(-transX, -transY);
                        canvas.concat(child.getMatrix());
                        canvas.translate(transX, transY);
                    }
                }

            }
        }
        return restoreTo;
    }
}