1 /* 2 * Copyright (C) 2016 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 com.google.android.setupdesign.view; 18 19 import android.annotation.TargetApi; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.os.Build.VERSION_CODES; 23 import android.util.AttributeSet; 24 import android.view.ViewGroup; 25 import android.widget.FrameLayout; 26 import com.google.android.setupcompat.partnerconfig.PartnerConfig; 27 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; 28 import com.google.android.setupcompat.util.BuildCompatUtils; 29 import com.google.android.setupdesign.R; 30 31 /** 32 * A FrameLayout subclass that has an "intrinsic size", which is the size it wants to be if that is 33 * within the constraints given by the parent. The intrinsic size can be set with the {@code 34 * android:width} and {@code android:height} attributes in XML. 35 * 36 * <p>Note that for the intrinsic size to be meaningful, {@code android:layout_width} and/or {@code 37 * android:layout_height} will need to be {@code wrap_content}. 38 */ 39 public class IntrinsicSizeFrameLayout extends FrameLayout { 40 41 private int intrinsicHeight = 0; 42 private int intrinsicWidth = 0; 43 IntrinsicSizeFrameLayout(Context context)44 public IntrinsicSizeFrameLayout(Context context) { 45 super(context); 46 init(context, null, 0); 47 } 48 IntrinsicSizeFrameLayout(Context context, AttributeSet attrs)49 public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 init(context, attrs, 0); 52 } 53 54 @TargetApi(VERSION_CODES.HONEYCOMB) IntrinsicSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)55 public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 56 super(context, attrs, defStyleAttr); 57 init(context, attrs, defStyleAttr); 58 } 59 init(Context context, AttributeSet attrs, int defStyleAttr)60 private void init(Context context, AttributeSet attrs, int defStyleAttr) { 61 if (isInEditMode()) { 62 return; 63 } 64 65 final TypedArray a = 66 context.obtainStyledAttributes( 67 attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0); 68 intrinsicHeight = 69 a.getDimensionPixelSize(R.styleable.SudIntrinsicSizeFrameLayout_android_height, 0); 70 intrinsicWidth = 71 a.getDimensionPixelSize(R.styleable.SudIntrinsicSizeFrameLayout_android_width, 0); 72 a.recycle(); 73 74 if (BuildCompatUtils.isAtLeastS()) { 75 if (PartnerConfigHelper.get(context) 76 .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT)) { 77 intrinsicHeight = 78 (int) 79 PartnerConfigHelper.get(context) 80 .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT); 81 } 82 if (PartnerConfigHelper.get(context) 83 .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH)) { 84 intrinsicWidth = 85 (int) 86 PartnerConfigHelper.get(context) 87 .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH); 88 } 89 } 90 } 91 92 @Override setLayoutParams(ViewGroup.LayoutParams params)93 public void setLayoutParams(ViewGroup.LayoutParams params) { 94 if (BuildCompatUtils.isAtLeastS()) { 95 // When both intrinsic height and width are 0, the card view style would be removed from 96 // foldable/tablet layout. It must set the layout width and height to MATCH_PARENT and then it 97 // can ignore the IntrinsicSizeFrameLayout from the foldable/tablet layout. 98 if (intrinsicHeight == 0 && intrinsicWidth == 0) { 99 params.width = ViewGroup.LayoutParams.MATCH_PARENT; 100 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 101 } 102 } 103 super.setLayoutParams(params); 104 } 105 106 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)107 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 108 super.onMeasure( 109 getIntrinsicMeasureSpec(widthMeasureSpec, intrinsicWidth), 110 getIntrinsicMeasureSpec(heightMeasureSpec, intrinsicHeight)); 111 } 112 getIntrinsicMeasureSpec(int measureSpec, int intrinsicSize)113 private int getIntrinsicMeasureSpec(int measureSpec, int intrinsicSize) { 114 if (intrinsicSize <= 0) { 115 // Intrinsic size is not set, just return the original spec 116 return measureSpec; 117 } 118 final int mode = MeasureSpec.getMode(measureSpec); 119 final int size = MeasureSpec.getSize(measureSpec); 120 if (mode == MeasureSpec.UNSPECIFIED) { 121 // Parent did not give any constraint, so we'll be the intrinsic size 122 return MeasureSpec.makeMeasureSpec(intrinsicHeight, MeasureSpec.EXACTLY); 123 } else if (mode == MeasureSpec.AT_MOST) { 124 // If intrinsic size is within parents constraint, take the intrinsic size. 125 // Otherwise take the parents size because that's closest to the intrinsic size. 126 return MeasureSpec.makeMeasureSpec(Math.min(size, intrinsicHeight), MeasureSpec.EXACTLY); 127 } 128 // Parent specified EXACTLY, or in all other cases, just return the original spec 129 return measureSpec; 130 } 131 } 132