e1d31e5406
Revert submission 33716851-revert-33672643-DataDeviceProfile1-XHYRAPLEBK Reason for revert: Error is fixed Reverted changes: /q/submissionid:33716851-revert-33672643-DataDeviceProfile1-XHYRAPLEBK Change-Id: Ib86824134955903c11e51e52dbfd6368aba1cd50
213 lines
7.7 KiB
Kotlin
213 lines
7.7 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 android.window.OnBackInvokedDispatcher
|
|
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 = context.getColor(R.color.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
|
|
/** Used to set whether users can tap outside the current tooltip window to dismiss it */
|
|
var allowTouchDismissal = true
|
|
|
|
/** 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.deviceProperties.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) &&
|
|
allowTouchDismissal
|
|
) {
|
|
close(true)
|
|
}
|
|
return false
|
|
}
|
|
|
|
override fun onAttachedToWindow() {
|
|
super.onAttachedToWindow()
|
|
findOnBackInvokedDispatcher()
|
|
?.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, this)
|
|
}
|
|
|
|
override fun onDetachedFromWindow() {
|
|
super.onDetachedFromWindow()
|
|
findOnBackInvokedDispatcher()?.unregisterOnBackInvokedCallback(this)
|
|
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)
|
|
}
|
|
}
|
|
}
|