1 /*
2  * Copyright (C) 2024 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.android.launcher3.apppairs
18 
19 import android.content.Context
20 import com.android.launcher3.BubbleTextView.DISPLAY_FOLDER
21 import com.android.launcher3.DeviceProfile
22 import com.android.launcher3.R
23 import com.android.launcher3.util.Themes
24 import com.android.launcher3.views.ActivityContext
25 
26 class AppPairIconDrawingParams(val context: Context, container: Int) {
27     companion object {
28         // Design specs -- the below ratios are in relation to the size of a standard app icon.
29         // Note: The standard app icon has two sizes. One is the full size of the drawable (returned
30         // by dp.iconSizePx), and one is the visual size of the icon on-screen (11/12 of that).
31         // Hence the calculations below.
32         const val STANDARD_ICON_PADDING = 1 / 24f
33         const val STANDARD_ICON_SHRINK = 1 - STANDARD_ICON_PADDING * 2
34         // App pairs are slightly smaller than the *visual* size of a standard icon, so all ratios
35         // are calculated with that in mind.
36         const val OUTER_PADDING_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
37         const val INNER_PADDING_SCALE = 1 / 24f * STANDARD_ICON_SHRINK
38         const val CENTER_CHANNEL_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
39         const val BIG_RADIUS_SCALE = 1 / 5f * STANDARD_ICON_SHRINK
40         const val SMALL_RADIUS_SCALE = 1 / 15f * STANDARD_ICON_SHRINK
41         const val MEMBER_ICON_SCALE = 11 / 30f * STANDARD_ICON_SHRINK
42     }
43 
44     // The size at which this graphic will be drawn.
45     val iconSize: Int
46     // Standard app icons are padded by this amount on each side.
47     val standardIconPadding: Float
48     // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
49     // each side.
50     val outerPadding: Float
51     // The colored background (two rectangles in a square area) is this big.
52     val backgroundSize: Float
53     // The size of the channel between the two halves of the app pair icon.
54     val centerChannelSize: Float
55     // The corner radius of the outside corners.
56     val bigRadius: Float
57     // The corner radius of the inside corners, touching the center channel.
58     val smallRadius: Float
59     // Inside of the icon, the two member apps are padded by this much.
60     val innerPadding: Float
61     // The two member apps have icons that are this big (in diameter).
62     val memberIconSize: Float
63     // The app pair icon appears differently in portrait and landscape.
64     var isLeftRightSplit: Boolean = true
65     // The background paint color (based on container).
66     var bgColor: Int = 0
67 
68     init {
69         val activity: ActivityContext = ActivityContext.lookupContext(context)
70         val dp = activity.deviceProfile
71         iconSize = if (container == DISPLAY_FOLDER) dp.folderChildIconSizePx else dp.iconSizePx
72         standardIconPadding = iconSize * STANDARD_ICON_PADDING
73         outerPadding = iconSize * OUTER_PADDING_SCALE
74         backgroundSize = iconSize * STANDARD_ICON_SHRINK - (outerPadding * 2)
75         centerChannelSize = iconSize * CENTER_CHANNEL_SCALE
76         bigRadius = iconSize * BIG_RADIUS_SCALE
77         smallRadius = iconSize * SMALL_RADIUS_SCALE
78         innerPadding = iconSize * INNER_PADDING_SCALE
79         memberIconSize = iconSize * MEMBER_ICON_SCALE
80         updateOrientation(dp)
81         updateBgColor(container)
82     }
83 
84     /** Checks the device orientation and updates isLeftRightSplit accordingly. */
updateOrientationnull85     fun updateOrientation(dp: DeviceProfile) {
86         isLeftRightSplit = dp.isLeftRightSplit
87     }
88 
updateBgColornull89     fun updateBgColor(container: Int) {
90         if (container == DISPLAY_FOLDER) {
91             bgColor = Themes.getAttrColor(context, R.attr.appPairSurfaceInFolder)
92         } else {
93             val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
94             bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
95             ta.recycle()
96         }
97     }
98 }
99