• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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