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.taskbar 18 19 import android.animation.Animator 20 import android.animation.AnimatorSet 21 import android.animation.ObjectAnimator 22 import android.animation.ValueAnimator 23 import android.content.Context 24 import android.view.View 25 import androidx.dynamicanimation.animation.SpringForce 26 import com.android.app.animation.Interpolators 27 import com.android.launcher3.LauncherAnimUtils 28 import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X 29 import com.android.launcher3.anim.SpringAnimationBuilder 30 import com.android.wm.shell.shared.bubbles.BubbleBarLocation 31 32 /** Animator helper that creates bars animators. */ 33 object BarsLocationAnimatorHelper { 34 const val FADE_OUT_ANIM_ALPHA_DURATION_MS: Long = 50L 35 const val FADE_OUT_ANIM_ALPHA_DELAY_MS: Long = 50L 36 const val FADE_OUT_ANIM_POSITION_DURATION_MS: Long = 100L 37 const val FADE_IN_ANIM_ALPHA_DURATION_MS: Long = 100L 38 39 // Use STIFFNESS_MEDIUMLOW which is not defined in the API constants 40 private const val FADE_IN_ANIM_POSITION_SPRING_STIFFNESS: Float = 400f 41 42 // During fade out animation we shift the bubble bar 1/80th of the screen width 43 private const val FADE_OUT_ANIM_POSITION_SHIFT: Float = 1 / 80f 44 45 // During fade in animation we shift the bubble bar 1/60th of the screen width 46 private const val FADE_IN_ANIM_POSITION_SHIFT: Float = 1 / 60f 47 48 private val Context.screenWidth: Int 49 get() = resources.displayMetrics.widthPixels 50 51 val Context.outShift: Float 52 get() = screenWidth * FADE_OUT_ANIM_POSITION_SHIFT 53 54 val Context.inShiftX: Float 55 get() = screenWidth * FADE_IN_ANIM_POSITION_SHIFT 56 57 /** 58 * Creates out animation for targetView that animates it finalTx and plays targetViewAlphaAnim 59 * to its final value. 60 */ createLocationOutAnimatornull61 private fun createLocationOutAnimator( 62 finalTx: Float, 63 targetViewAlphaAnim: ObjectAnimator, 64 targetView: View, 65 ): Animator { 66 val positionAnim = 67 ObjectAnimator.ofFloat(targetView, VIEW_TRANSLATE_X, finalTx) 68 .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS) 69 positionAnim.interpolator = Interpolators.EMPHASIZED_ACCELERATE 70 71 targetViewAlphaAnim.setDuration(FADE_OUT_ANIM_ALPHA_DURATION_MS) 72 targetViewAlphaAnim.startDelay = FADE_OUT_ANIM_ALPHA_DELAY_MS 73 74 val animatorSet = AnimatorSet() 75 animatorSet.playTogether(positionAnim, targetViewAlphaAnim) 76 return animatorSet 77 } 78 79 /** 80 * Creates in animation for targetView that animates it from startTx to finalTx and plays 81 * targetViewAlphaAnim to its final value. 82 */ createLocationInAnimatornull83 private fun createLocationInAnimator( 84 startTx: Float, 85 finalTx: Float, 86 targetViewAlphaAnim: ObjectAnimator, 87 targetView: View, 88 ): Animator { 89 targetViewAlphaAnim.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS) 90 val positionAnim: ValueAnimator = 91 SpringAnimationBuilder(targetView.context) 92 .setStartValue(startTx) 93 .setEndValue(finalTx) 94 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) 95 .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS) 96 .build(targetView, VIEW_TRANSLATE_X) 97 val animatorSet = AnimatorSet() 98 animatorSet.playTogether(positionAnim, targetViewAlphaAnim) 99 return animatorSet 100 } 101 102 /** Creates an animator for the bubble bar view in part. */ 103 @JvmStatic getBubbleBarLocationInAnimatornull104 fun getBubbleBarLocationInAnimator( 105 newLocation: BubbleBarLocation, 106 currentLocation: BubbleBarLocation, 107 distanceFromOtherSide: Float, 108 targetViewAlphaAnim: ObjectAnimator, 109 bubbleBarView: View, 110 ): Animator { 111 val shift: Float = bubbleBarView.context.outShift 112 113 val onLeft = newLocation.isOnLeft(bubbleBarView.isLayoutRtl) 114 val startTx: Float 115 val finalTx = 116 if (newLocation == currentLocation) { 117 // Animated location matches layout location. 118 0f 119 } else { 120 // We are animating in to a transient location, need to move the bar 121 // accordingly. 122 distanceFromOtherSide * (if (onLeft) -1 else 1) 123 } 124 startTx = 125 if (onLeft) { 126 // Bar will be shown on the left side. Start point is shifted right. 127 finalTx + shift 128 } else { 129 // Bar will be shown on the right side. Start point is shifted left. 130 finalTx - shift 131 } 132 return createLocationInAnimator(startTx, finalTx, targetViewAlphaAnim, bubbleBarView) 133 } 134 135 /** 136 * Creates an animator for the bubble bar view out part. 137 * 138 * @param targetLocation the location bubble bar should animate to. 139 */ 140 @JvmStatic getBubbleBarLocationOutAnimatornull141 fun getBubbleBarLocationOutAnimator( 142 bubbleBarView: View, 143 targetLocation: BubbleBarLocation, 144 targetViewAlphaAnim: ObjectAnimator, 145 ): Animator { 146 val onLeft = targetLocation.isOnLeft(bubbleBarView.isLayoutRtl) 147 val shift = bubbleBarView.context.outShift 148 val finalTx = bubbleBarView.translationX + (if (onLeft) -shift else shift) 149 return this.createLocationOutAnimator(finalTx, targetViewAlphaAnim, bubbleBarView) 150 } 151 152 /** Creates a teleport animator for the navigation buttons view. */ 153 @JvmStatic getTeleportAnimatorForNavButtonsnull154 fun getTeleportAnimatorForNavButtons( 155 location: BubbleBarLocation, 156 navButtonsView: View, 157 navBarTargetTranslationX: Float, 158 ): Animator { 159 val outShift: Float = navButtonsView.context.outShift 160 val isNavBarOnRight: Boolean = location.isOnLeft(navButtonsView.isLayoutRtl) 161 val finalOutTx = 162 navButtonsView.translationX + (if (isNavBarOnRight) outShift else -outShift) 163 val fadeout: Animator = 164 createLocationOutAnimator( 165 finalOutTx, 166 ObjectAnimator.ofFloat(navButtonsView, LauncherAnimUtils.VIEW_ALPHA, 0f), 167 navButtonsView, 168 ) 169 val inShift: Float = navButtonsView.context.inShiftX 170 val inStartX = navBarTargetTranslationX + (if (isNavBarOnRight) -inShift else inShift) 171 val fadeIn: Animator = 172 createLocationInAnimator( 173 inStartX, 174 navBarTargetTranslationX, 175 ObjectAnimator.ofFloat(navButtonsView, LauncherAnimUtils.VIEW_ALPHA, 1f), 176 navButtonsView, 177 ) 178 val teleportAnimator = AnimatorSet() 179 teleportAnimator.play(fadeout).before(fadeIn) 180 return teleportAnimator 181 } 182 } 183