1 /* <lambda>null2 * Copyright (C) 2023 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 18 package com.android.wallpaper.picker 19 20 import android.content.Context 21 import android.util.AttributeSet 22 import android.widget.LinearLayout 23 import androidx.core.view.children 24 import androidx.core.view.updateLayoutParams 25 import com.android.wallpaper.util.ScreenSizeCalculator 26 27 /** 28 * [LinearLayout] that sizes its children using a fixed aspect ratio that is the same as that of the 29 * display, and can lay out multiple children horizontally with margin 30 */ 31 class DisplayAspectRatioLinearLayout( 32 context: Context, 33 attrs: AttributeSet?, 34 ) : LinearLayout(context, attrs) { 35 36 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 37 super.onMeasure(widthMeasureSpec, heightMeasureSpec) 38 39 val screenAspectRatio = ScreenSizeCalculator.getInstance().getScreenAspectRatio(context) 40 val parentWidth = this.measuredWidth 41 val parentHeight = this.measuredHeight 42 val itemSpacingPx = ITEM_SPACING_DP.toPx(context.resources.displayMetrics.density) 43 val (childWidth, childHeight) = 44 if (orientation == HORIZONTAL) { 45 val availableWidth = 46 parentWidth - paddingStart - paddingEnd - (childCount - 1) * itemSpacingPx 47 val availableHeight = parentHeight - paddingTop - paddingBottom 48 var width = availableWidth / childCount 49 var height = (width * screenAspectRatio).toInt() 50 if (height > availableHeight) { 51 height = availableHeight 52 width = (height / screenAspectRatio).toInt() 53 } 54 width to height 55 } else { 56 val availableWidth = parentWidth - paddingStart - paddingEnd 57 val availableHeight = 58 parentHeight - paddingTop - paddingBottom - (childCount - 1) * itemSpacingPx 59 var height = availableHeight / childCount 60 var width = (height / screenAspectRatio).toInt() 61 if (width > availableWidth) { 62 width = availableWidth 63 height = (width * screenAspectRatio).toInt() 64 } 65 width to height 66 } 67 68 val itemSpacingHalfPx = ITEM_SPACING_DP_HALF.toPx(context.resources.displayMetrics.density) 69 children.forEachIndexed { index, child -> 70 val addSpacingToStart = index > 0 71 val addSpacingToEnd = index < (childCount - 1) 72 if (orientation == HORIZONTAL) { 73 child.updateLayoutParams<MarginLayoutParams> { 74 if (addSpacingToStart) this.marginStart = itemSpacingHalfPx 75 if (addSpacingToEnd) this.marginEnd = itemSpacingHalfPx 76 } 77 } else { 78 child.updateLayoutParams<MarginLayoutParams> { 79 if (addSpacingToStart) this.topMargin = itemSpacingHalfPx 80 if (addSpacingToEnd) this.bottomMargin = itemSpacingHalfPx 81 } 82 } 83 84 child.measure( 85 MeasureSpec.makeMeasureSpec( 86 childWidth, 87 MeasureSpec.EXACTLY, 88 ), 89 MeasureSpec.makeMeasureSpec( 90 childHeight, 91 MeasureSpec.EXACTLY, 92 ), 93 ) 94 } 95 } 96 97 private fun Int.toPx(density: Float): Int { 98 return (this * density).toInt() 99 } 100 101 companion object { 102 private const val ITEM_SPACING_DP = 12 103 private const val ITEM_SPACING_DP_HALF = ITEM_SPACING_DP / 2 104 } 105 } 106