1 /*
2  * Copyright (C) 2014 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 
17 package androidx.appcompat.widget;
18 
19 import static android.view.View.MeasureSpec.AT_MOST;
20 import static android.view.View.MeasureSpec.EXACTLY;
21 import static android.view.View.MeasureSpec.getMode;
22 
23 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
24 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
25 
26 import android.content.Context;
27 import android.graphics.Rect;
28 import android.util.AttributeSet;
29 import android.util.DisplayMetrics;
30 import android.util.TypedValue;
31 import android.widget.FrameLayout;
32 
33 import androidx.annotation.RestrictTo;
34 import androidx.core.view.ViewCompat;
35 
36 /**
37  * @hide
38  */
39 @RestrictTo(LIBRARY)
40 public class ContentFrameLayout extends FrameLayout {
41 
42     public interface OnAttachListener {
onDetachedFromWindow()43         void onDetachedFromWindow();
onAttachedFromWindow()44         void onAttachedFromWindow();
45     }
46 
47     private TypedValue mMinWidthMajor;
48     private TypedValue mMinWidthMinor;
49     private TypedValue mFixedWidthMajor;
50     private TypedValue mFixedWidthMinor;
51     private TypedValue mFixedHeightMajor;
52     private TypedValue mFixedHeightMinor;
53 
54     private final Rect mDecorPadding;
55 
56     private OnAttachListener mAttachListener;
57 
ContentFrameLayout(Context context)58     public ContentFrameLayout(Context context) {
59         this(context, null);
60     }
61 
ContentFrameLayout(Context context, AttributeSet attrs)62     public ContentFrameLayout(Context context, AttributeSet attrs) {
63         this(context, attrs, 0);
64     }
65 
ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)66     public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
67         super(context, attrs, defStyleAttr);
68         mDecorPadding = new Rect();
69     }
70 
71     /**
72      * @hide
73      */
74     @RestrictTo(LIBRARY_GROUP)
dispatchFitSystemWindows(Rect insets)75     public void dispatchFitSystemWindows(Rect insets) {
76         fitSystemWindows(insets);
77     }
78 
setAttachListener(OnAttachListener attachListener)79     public void setAttachListener(OnAttachListener attachListener) {
80         mAttachListener = attachListener;
81     }
82 
83     /**
84      * Notify this view of the window decor view's padding. We use these values when working out
85      * our size for the window size attributes.
86      *
87      * @hide
88      */
89     @RestrictTo(LIBRARY_GROUP)
setDecorPadding(int left, int top, int right, int bottom)90     public void setDecorPadding(int left, int top, int right, int bottom) {
91         mDecorPadding.set(left, top, right, bottom);
92         if (ViewCompat.isLaidOut(this)) {
93             requestLayout();
94         }
95     }
96 
97     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)98     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
99         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
100         final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
101 
102         final int widthMode = getMode(widthMeasureSpec);
103         final int heightMode = getMode(heightMeasureSpec);
104 
105         boolean fixedWidth = false;
106         if (widthMode == AT_MOST) {
107             final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
108             if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
109                 int w = 0;
110                 if (tvw.type == TypedValue.TYPE_DIMENSION) {
111                     w = (int) tvw.getDimension(metrics);
112                 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
113                     w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
114                 }
115                 if (w > 0) {
116                     w -= (mDecorPadding.left + mDecorPadding.right);
117                     final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
118                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(
119                             Math.min(w, widthSize), EXACTLY);
120                     fixedWidth = true;
121                 }
122             }
123         }
124 
125         if (heightMode == AT_MOST) {
126             final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
127             if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
128                 int h = 0;
129                 if (tvh.type == TypedValue.TYPE_DIMENSION) {
130                     h = (int) tvh.getDimension(metrics);
131                 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
132                     h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
133                 }
134                 if (h > 0) {
135                     h -= (mDecorPadding.top + mDecorPadding.bottom);
136                     final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
137                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(
138                             Math.min(h, heightSize), EXACTLY);
139                 }
140             }
141         }
142 
143         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
144 
145         int width = getMeasuredWidth();
146         boolean measure = false;
147 
148         widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
149 
150         if (!fixedWidth && widthMode == AT_MOST) {
151             final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
152             if (tv != null && tv.type != TypedValue.TYPE_NULL) {
153                 int min = 0;
154                 if (tv.type == TypedValue.TYPE_DIMENSION) {
155                     min = (int) tv.getDimension(metrics);
156                 } else if (tv.type == TypedValue.TYPE_FRACTION) {
157                     min = (int) tv.getFraction(metrics.widthPixels, metrics.widthPixels);
158                 }
159                 if (min > 0) {
160                     min -= (mDecorPadding.left + mDecorPadding.right);
161                 }
162                 if (width < min) {
163                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
164                     measure = true;
165                 }
166             }
167         }
168 
169         if (measure) {
170             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
171         }
172     }
173 
getMinWidthMajor()174     public TypedValue getMinWidthMajor() {
175         if (mMinWidthMajor == null) mMinWidthMajor = new TypedValue();
176         return mMinWidthMajor;
177     }
178 
getMinWidthMinor()179     public TypedValue getMinWidthMinor() {
180         if (mMinWidthMinor == null) mMinWidthMinor = new TypedValue();
181         return mMinWidthMinor;
182     }
183 
getFixedWidthMajor()184     public TypedValue getFixedWidthMajor() {
185         if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
186         return mFixedWidthMajor;
187     }
188 
getFixedWidthMinor()189     public TypedValue getFixedWidthMinor() {
190         if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
191         return mFixedWidthMinor;
192     }
193 
getFixedHeightMajor()194     public TypedValue getFixedHeightMajor() {
195         if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
196         return mFixedHeightMajor;
197     }
198 
getFixedHeightMinor()199     public TypedValue getFixedHeightMinor() {
200         if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
201         return mFixedHeightMinor;
202     }
203 
204     @Override
onAttachedToWindow()205     protected void onAttachedToWindow() {
206         super.onAttachedToWindow();
207         if (mAttachListener != null) {
208             mAttachListener.onAttachedFromWindow();
209         }
210     }
211 
212     @Override
onDetachedFromWindow()213     protected void onDetachedFromWindow() {
214         super.onDetachedFromWindow();
215         if (mAttachListener != null) {
216             mAttachListener.onDetachedFromWindow();
217         }
218     }
219 }
220