blob: dce97eb594e218f6d584b798e1f0c713ca5f752f [file] [log] [blame]
/*
* 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.util.AttributeSet
import android.view.Gravity
import android.widget.FrameLayout
import androidx.annotation.OpenForTesting
import com.android.launcher3.DeviceProfile
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.model.data.AppPairInfo
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
/**
* 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.
*/
@OpenForTesting
open class AppPairIconGraphic
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs), OnDeviceProfileChangeListener {
private val TAG = "AppPairIconGraphic"
companion object {
/** Composes a drawable for this icon, consisting of a background and 2 app icons. */
@JvmStatic
fun composeDrawable(
appPairInfo: AppPairInfo,
p: AppPairIconDrawingParams
): AppPairIconDrawable {
// Generate new icons, using themed flag if needed.
val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0
val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, flags)
val appIcon2 = appPairInfo.getSecondApp().newIcon(p.context, flags)
appIcon1.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
appIcon2.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
// If icons are unlaunchable due to screen size, manually override disabled appearance.
// (otherwise, leave disabled state alone; icons will naturally inherit the app's state)
val (isApp1Launchable, isApp2Launchable) = appPairInfo.isLaunchable(p.context)
if (!isApp1Launchable) appIcon1.setIsDisabled(true)
if (!isApp2Launchable) appIcon2.setIsDisabled(true)
// Create icon drawable.
val fullIconDrawable = AppPairIconDrawable(p, appIcon1, appIcon2)
fullIconDrawable.setBounds(0, 0, p.iconSize, p.iconSize)
return fullIconDrawable
}
}
private lateinit var parentIcon: AppPairIcon
private lateinit var drawParams: AppPairIconDrawingParams
lateinit var drawable: AppPairIconDrawable
fun init(icon: AppPairIcon, container: Int) {
parentIcon = icon
drawParams = AppPairIconDrawingParams(context, container)
drawable = composeDrawable(icon.info, drawParams)
// Center the drawable area in the larger icon canvas
val lp: LayoutParams = layoutParams as LayoutParams
lp.gravity = Gravity.CENTER_HORIZONTAL
lp.height = drawParams.iconSize
lp.width = drawParams.iconSize
layoutParams = lp
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
getActivityContext().addOnDeviceProfileChangeListener(this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
getActivityContext().removeOnDeviceProfileChangeListener(this)
}
private fun getActivityContext(): ActivityContext {
return ActivityContext.lookupContext(context)
}
/** When device profile changes, update orientation */
override fun onDeviceProfileChanged(dp: DeviceProfile) {
drawParams.updateOrientation(dp)
redraw()
}
/**
* When the icon is temporary moved to a different colored surface, update the background color.
* Calling this method with [null] reverts the icon back to its default color.
*/
fun onTemporaryContainerChange(newContainer: Int?) {
drawParams.updateBgColor(newContainer ?: parentIcon.container)
redraw()
}
/**
* Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system.
*/
fun getIconBounds(outBounds: Rect) {
outBounds.set(0, 0, drawParams.backgroundSize.toInt(), drawParams.backgroundSize.toInt())
outBounds.offset(
// x-coordinate in parent's coordinate system
((parentIcon.width - drawParams.backgroundSize) / 2).toInt(),
// y-coordinate in parent's coordinate system
(parentIcon.paddingTop + drawParams.standardIconPadding + drawParams.outerPadding)
.toInt()
)
}
/** Updates the icon drawable and redraws it */
fun redraw() {
drawable = composeDrawable(parentIcon.info, drawParams)
invalidate()
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
drawable.draw(canvas)
}
}