1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.widget;
18
19import android.graphics.drawable.Drawable;
20import android.media.update.ViewGroupProvider;
21import android.util.AttributeSet;
22import android.view.View;
23import android.view.ViewGroup;
24import android.view.ViewGroup.LayoutParams;
25import android.view.ViewGroup.MarginLayoutParams;
26
27import java.util.ArrayList;
28
29public class BaseLayout extends ViewGroupImpl {
30    private final ViewGroup mInstance;
31    private final ViewGroupProvider mSuperProvider;
32    private final ViewGroupProvider mPrivateProvider;
33
34    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
35
36    public BaseLayout(ViewGroup instance,
37            ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
38        super(instance, superProvider, privateProvider);
39        mInstance = instance;
40        mSuperProvider = superProvider;
41        mPrivateProvider = privateProvider;
42    }
43
44    @Override
45    public boolean checkLayoutParams_impl(LayoutParams p) {
46        return p instanceof MarginLayoutParams;
47    }
48
49    @Override
50    public LayoutParams generateDefaultLayoutParams_impl() {
51        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
52    }
53
54    @Override
55    public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
56        return new MarginLayoutParams(mInstance.getContext(), attrs);
57    }
58
59    @Override
60    public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
61        if (lp instanceof MarginLayoutParams) {
62            return lp;
63        }
64        return new MarginLayoutParams(lp);
65    }
66
67    @Override
68    public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
69        int count = mInstance.getChildCount();
70
71        final boolean measureMatchParentChildren =
72                View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY ||
73                        View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY;
74        mMatchParentChildren.clear();
75
76        int maxHeight = 0;
77        int maxWidth = 0;
78        int childState = 0;
79
80        for (int i = 0; i < count; i++) {
81            final View child = mInstance.getChildAt(i);
82            if (child.getVisibility() != View.GONE) {
83                mPrivateProvider.measureChildWithMargins_impl(
84                        child, widthMeasureSpec, 0, heightMeasureSpec, 0);
85                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
86                maxWidth = Math.max(maxWidth,
87                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
88                maxHeight = Math.max(maxHeight,
89                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
90                childState = childState | child.getMeasuredState();
91                if (measureMatchParentChildren) {
92                    if (lp.width == LayoutParams.MATCH_PARENT ||
93                            lp.height == LayoutParams.MATCH_PARENT) {
94                        mMatchParentChildren.add(child);
95                    }
96                }
97            }
98        }
99
100        // Account for padding too
101        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
102        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
103
104        // Check against our minimum height and width
105        maxHeight = Math.max(maxHeight, mPrivateProvider.getSuggestedMinimumHeight_impl());
106        maxWidth = Math.max(maxWidth, mPrivateProvider.getSuggestedMinimumWidth_impl());
107
108        // Check against our foreground's minimum height and width
109        final Drawable drawable = mInstance.getForeground();
110        if (drawable != null) {
111            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
112            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
113        }
114
115        mPrivateProvider.setMeasuredDimension_impl(
116                mInstance.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
117                mInstance.resolveSizeAndState(maxHeight, heightMeasureSpec,
118                        childState << View.MEASURED_HEIGHT_STATE_SHIFT));
119
120        count = mMatchParentChildren.size();
121        if (count > 1) {
122            for (int i = 0; i < count; i++) {
123                final View child = mMatchParentChildren.get(i);
124                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
125
126                final int childWidthMeasureSpec;
127                if (lp.width == LayoutParams.MATCH_PARENT) {
128                    final int width = Math.max(0, mInstance.getMeasuredWidth()
129                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
130                            - lp.leftMargin - lp.rightMargin);
131                    childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
132                            width, View.MeasureSpec.EXACTLY);
133                } else {
134                    childWidthMeasureSpec = mInstance.getChildMeasureSpec(widthMeasureSpec,
135                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
136                                    lp.leftMargin + lp.rightMargin,
137                            lp.width);
138                }
139
140                final int childHeightMeasureSpec;
141                if (lp.height == LayoutParams.MATCH_PARENT) {
142                    final int height = Math.max(0, mInstance.getMeasuredHeight()
143                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
144                            - lp.topMargin - lp.bottomMargin);
145                    childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
146                            height, View.MeasureSpec.EXACTLY);
147                } else {
148                    childHeightMeasureSpec = mInstance.getChildMeasureSpec(heightMeasureSpec,
149                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
150                                    lp.topMargin + lp.bottomMargin,
151                            lp.height);
152                }
153
154                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
155            }
156        }
157    }
158
159    @Override
160    public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
161        final int count = mInstance.getChildCount();
162
163        final int parentLeft = getPaddingLeftWithForeground();
164        final int parentRight = right - left - getPaddingRightWithForeground();
165
166        final int parentTop = getPaddingTopWithForeground();
167        final int parentBottom = bottom - top - getPaddingBottomWithForeground();
168
169        for (int i = 0; i < count; i++) {
170            final View child = mInstance.getChildAt(i);
171            if (child.getVisibility() != View.GONE) {
172                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
173
174                final int width = child.getMeasuredWidth();
175                final int height = child.getMeasuredHeight();
176
177                int childLeft;
178                int childTop;
179
180                childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
181                        lp.leftMargin - lp.rightMargin;
182
183                childTop = parentTop + (parentBottom - parentTop - height) / 2 +
184                        lp.topMargin - lp.bottomMargin;
185
186                child.layout(childLeft, childTop, childLeft + width, childTop + height);
187            }
188        }
189    }
190
191    @Override
192    public boolean shouldDelayChildPressedState_impl() {
193        return false;
194    }
195
196    private int getPaddingLeftWithForeground() {
197        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingLeft(), 0) :
198                mInstance.getPaddingLeft() + 0;
199    }
200
201    private int getPaddingRightWithForeground() {
202        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingRight(), 0) :
203                mInstance.getPaddingRight() + 0;
204    }
205
206    private int getPaddingTopWithForeground() {
207        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingTop(), 0) :
208                mInstance.getPaddingTop() + 0;
209    }
210
211    private int getPaddingBottomWithForeground() {
212        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingBottom(), 0) :
213                mInstance.getPaddingBottom() + 0;
214    }
215}
216