summaryrefslogtreecommitdiff
path: root/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/apppairs/AppPairIconGraphic.kt')
-rw-r--r--src/com/android/launcher3/apppairs/AppPairIconGraphic.kt141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
new file mode 100644
index 0000000000..29459796bc
--- /dev/null
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.apppairs
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.Gravity
+import android.widget.FrameLayout
+import com.android.launcher3.DeviceProfile
+
+/**
+ * A FrameLayout marking the area on an [AppPairIcon] where the visual icon will be drawn. One of
+ * two child UI elements on an [AppPairIcon], along with a BubbleTextView holding the text title.
+ */
+class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ FrameLayout(context, attrs) {
+ companion object {
+ // Design specs -- the below ratios are in relation to the size of a standard app icon.
+ private const val OUTER_PADDING_SCALE = 1 / 30f
+ private const val INNER_PADDING_SCALE = 1 / 24f
+ private const val MEMBER_ICON_SCALE = 11 / 30f
+ private const val CENTER_CHANNEL_SCALE = 1 / 30f
+ private const val BIG_RADIUS_SCALE = 1 / 5f
+ private const val SMALL_RADIUS_SCALE = 1 / 15f
+ }
+
+ // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
+ // each side.
+ private var outerPadding = 0f
+ // Inside of the icon, the two member apps are padded by this much.
+ private var innerPadding = 0f
+ // The colored background (two rectangles in a square area) is this big.
+ private var backgroundSize = 0f
+ // The two member apps have icons that are this big (in diameter).
+ private var memberIconSize = 0f
+ // The size of the center channel.
+ var centerChannelSize = 0f
+ // The large outer radius of the background rectangles.
+ var bigRadius = 0f
+ // The small inner radius of the background rectangles.
+ var smallRadius = 0f
+ // The app pairs icon appears differently in portrait and landscape.
+ var isLeftRightSplit = false
+
+ private lateinit var parentIcon: AppPairIcon
+ private lateinit var appPairBackground: Drawable
+ private lateinit var appIcon1: Drawable
+ private lateinit var appIcon2: Drawable
+
+ fun init(grid: DeviceProfile, icon: AppPairIcon) {
+ // Calculate device-specific measurements
+ val defaultIconSize = grid.iconSizePx
+ outerPadding = OUTER_PADDING_SCALE * defaultIconSize
+ innerPadding = INNER_PADDING_SCALE * defaultIconSize
+ backgroundSize = defaultIconSize - outerPadding * 2
+ memberIconSize = MEMBER_ICON_SCALE * defaultIconSize
+ centerChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize
+ bigRadius = BIG_RADIUS_SCALE * defaultIconSize
+ smallRadius = SMALL_RADIUS_SCALE * defaultIconSize
+ isLeftRightSplit = grid.isLeftRightSplit
+ parentIcon = icon
+
+ appPairBackground = AppPairIconBackground(context, this)
+ appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
+ appIcon1 = parentIcon.info.contents[0].newIcon(context)
+ appIcon2 = parentIcon.info.contents[1].newIcon(context)
+ appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ }
+
+ /** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */
+ fun getIconBounds(outBounds: Rect) {
+ outBounds.set(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
+ outBounds.offset(
+ // x-coordinate in parent's coordinate system
+ ((parentIcon.width - backgroundSize) / 2).toInt(),
+ // y-coordinate in parent's coordinate system
+ parentIcon.paddingTop + outerPadding.toInt()
+ )
+ }
+
+ override fun dispatchDraw(canvas: Canvas) {
+ super.dispatchDraw(canvas)
+
+ // Center the drawable area in the larger icon canvas
+ val lp: LayoutParams = layoutParams as LayoutParams
+ lp.gravity = Gravity.CENTER_HORIZONTAL
+ lp.topMargin = outerPadding.toInt()
+ lp.height = backgroundSize.toInt()
+ lp.width = backgroundSize.toInt()
+ layoutParams = lp
+
+ // Draw background
+ appPairBackground.draw(canvas)
+
+ // Draw first icon
+ canvas.save()
+ // The app icons are placed differently depending on device orientation.
+ if (isLeftRightSplit) {
+ canvas.translate(innerPadding, height / 2f - memberIconSize / 2f)
+ } else {
+ canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
+ }
+ appIcon1.draw(canvas)
+ canvas.restore()
+
+ // Draw second icon
+ canvas.save()
+ // The app icons are placed differently depending on device orientation.
+ if (isLeftRightSplit) {
+ canvas.translate(
+ width - (innerPadding + memberIconSize),
+ height / 2f - memberIconSize / 2f
+ )
+ } else {
+ canvas.translate(
+ width / 2f - memberIconSize / 2f,
+ height - (innerPadding + memberIconSize)
+ )
+ }
+ appIcon2.draw(canvas)
+ canvas.restore()
+ }
+}