mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-28 15:56:49 +00:00
Fix kotlin nullable errors that were exposed by setting the retention of android.annotation.NonNull and android.annotation.Nullable to class retention. This relands I26edfec35dca14abe90b08e3c74de0446eda95d2 with a fix in SplitSelectDataHolder.kt to call createPackageContext when user is null instead of asserting that it is not null. Bug: 294110802 Test: builds Test: WMShellFlickerServiceTests Change-Id: I4525d0fa83a1db9cc5cff90f340fc3f863537c01
202 lines
7.3 KiB
Kotlin
202 lines
7.3 KiB
Kotlin
/*
|
|
* 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.taskbar
|
|
|
|
import android.animation.AnimatorSet
|
|
import android.animation.ValueAnimator
|
|
import android.content.Context
|
|
import android.provider.Settings
|
|
import android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING
|
|
import android.util.AttributeSet
|
|
import android.view.MotionEvent
|
|
import android.view.MotionEvent.ACTION_DOWN
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
import android.view.animation.Interpolator
|
|
import androidx.core.view.updateLayoutParams
|
|
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
|
|
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
|
|
import com.android.app.animation.Interpolators.STANDARD
|
|
import com.android.launcher3.AbstractFloatingView
|
|
import com.android.launcher3.R
|
|
import com.android.launcher3.anim.AnimatorListeners
|
|
import com.android.launcher3.popup.RoundedArrowDrawable
|
|
import com.android.launcher3.util.Themes
|
|
import com.android.launcher3.views.ActivityContext
|
|
|
|
private const val ENTER_DURATION_MS = 300L
|
|
private const val EXIT_DURATION_MS = 150L
|
|
|
|
/** Floating tooltip for Taskbar education. */
|
|
class TaskbarEduTooltip
|
|
@JvmOverloads
|
|
constructor(
|
|
context: Context,
|
|
attrs: AttributeSet? = null,
|
|
defStyleAttr: Int = 0,
|
|
) : AbstractFloatingView(context, attrs, defStyleAttr) {
|
|
|
|
private val activityContext: ActivityContext = ActivityContext.lookupContext(context)
|
|
|
|
private val backgroundColor =
|
|
Themes.getAttrColor(context, com.android.internal.R.attr.materialColorSurfaceBright)
|
|
|
|
private val tooltipCornerRadius = Themes.getDialogCornerRadius(context)
|
|
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
|
|
private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
|
|
private val arrowPointRadius = resources.getDimension(R.dimen.popup_arrow_corner_radius)
|
|
|
|
private val enterYDelta = resources.getDimension(R.dimen.taskbar_edu_tooltip_enter_y_delta)
|
|
private val exitYDelta = resources.getDimension(R.dimen.taskbar_edu_tooltip_exit_y_delta)
|
|
|
|
/** Container where the tooltip's body should be inflated. */
|
|
lateinit var content: ViewGroup
|
|
private set
|
|
private lateinit var arrow: View
|
|
|
|
/** Callback invoked when the tooltip is being closed. */
|
|
var onCloseCallback: () -> Unit = {}
|
|
private var openCloseAnimator: AnimatorSet? = null
|
|
|
|
/** Animates the tooltip into view. */
|
|
fun show() {
|
|
if (isOpen) {
|
|
return
|
|
}
|
|
mIsOpen = true
|
|
activityContext.dragLayer.addView(this)
|
|
|
|
// Make sure we have enough height to display all of the content, which can be an issue on
|
|
// large text and display scaling configurations. If we run out of height, remove the width
|
|
// constraint to reduce the number of lines of text and hopefully free up some height.
|
|
activityContext.dragLayer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
|
|
if (
|
|
measuredHeight + activityContext.deviceProfile.taskbarHeight >=
|
|
activityContext.deviceProfile.availableHeightPx
|
|
) {
|
|
updateLayoutParams { width = MATCH_PARENT }
|
|
}
|
|
|
|
openCloseAnimator = createOpenCloseAnimator(isOpening = true).apply { start() }
|
|
}
|
|
|
|
override fun onFinishInflate() {
|
|
super.onFinishInflate()
|
|
|
|
content = requireViewById(R.id.content)
|
|
arrow = requireViewById(R.id.arrow)
|
|
arrow.background =
|
|
RoundedArrowDrawable(
|
|
arrowWidth,
|
|
arrowHeight,
|
|
arrowPointRadius,
|
|
tooltipCornerRadius,
|
|
measuredWidth.toFloat(),
|
|
measuredHeight.toFloat(),
|
|
(measuredWidth - arrowWidth) / 2, // arrowOffsetX
|
|
0f, // arrowOffsetY
|
|
false, // isPointingUp
|
|
true, // leftAligned
|
|
backgroundColor,
|
|
)
|
|
}
|
|
|
|
override fun handleClose(animate: Boolean) {
|
|
if (!isOpen) {
|
|
return
|
|
}
|
|
|
|
onCloseCallback()
|
|
if (!animate) {
|
|
return closeComplete()
|
|
}
|
|
|
|
openCloseAnimator?.cancel()
|
|
openCloseAnimator = createOpenCloseAnimator(isOpening = false)
|
|
openCloseAnimator?.addListener(AnimatorListeners.forEndCallback(this::closeComplete))
|
|
openCloseAnimator?.start()
|
|
}
|
|
|
|
override fun isOfType(type: Int): Boolean = type and TYPE_TASKBAR_EDUCATION_DIALOG != 0
|
|
|
|
override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
|
if (ev?.action == ACTION_DOWN && !activityContext.dragLayer.isEventOverView(this, ev)) {
|
|
close(true)
|
|
}
|
|
return false
|
|
}
|
|
|
|
override fun onDetachedFromWindow() {
|
|
super.onDetachedFromWindow()
|
|
Settings.Secure.putInt(mContext.contentResolver, LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0)
|
|
}
|
|
|
|
private fun closeComplete() {
|
|
openCloseAnimator?.cancel()
|
|
openCloseAnimator = null
|
|
mIsOpen = false
|
|
activityContext.dragLayer.removeView(this)
|
|
}
|
|
|
|
private fun createOpenCloseAnimator(isOpening: Boolean): AnimatorSet {
|
|
val duration: Long
|
|
val alphaValues: FloatArray
|
|
val translateYValues: FloatArray
|
|
val fadeInterpolator: Interpolator
|
|
val translateYInterpolator: Interpolator
|
|
|
|
if (isOpening) {
|
|
duration = ENTER_DURATION_MS
|
|
alphaValues = floatArrayOf(0f, 1f)
|
|
translateYValues = floatArrayOf(enterYDelta, 0f)
|
|
fadeInterpolator = STANDARD
|
|
translateYInterpolator = EMPHASIZED_DECELERATE
|
|
} else {
|
|
duration = EXIT_DURATION_MS
|
|
alphaValues = floatArrayOf(1f, 0f)
|
|
translateYValues = floatArrayOf(0f, exitYDelta)
|
|
fadeInterpolator = EMPHASIZED_ACCELERATE
|
|
translateYInterpolator = EMPHASIZED_ACCELERATE
|
|
}
|
|
|
|
val fade =
|
|
ValueAnimator.ofFloat(*alphaValues).apply {
|
|
interpolator = fadeInterpolator
|
|
addUpdateListener {
|
|
val alpha = it.animatedValue as Float
|
|
content.alpha = alpha
|
|
arrow.alpha = alpha
|
|
}
|
|
}
|
|
|
|
val translateY =
|
|
ValueAnimator.ofFloat(*translateYValues).apply {
|
|
interpolator = translateYInterpolator
|
|
addUpdateListener {
|
|
val translationY = it.animatedValue as Float
|
|
content.translationY = translationY
|
|
arrow.translationY = translationY
|
|
}
|
|
}
|
|
|
|
return AnimatorSet().apply {
|
|
this.duration = duration
|
|
playTogether(fade, translateY)
|
|
}
|
|
}
|
|
}
|