Animate launcher icon in return to home gesture animation

This commit is contained in:
MM20 2022-10-08 18:41:14 +02:00
parent 709a36fb31
commit a9032735f2
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
7 changed files with 87 additions and 30 deletions

View File

@ -9,8 +9,9 @@ class LauncherActivity: SharedLauncherActivity(LauncherActivityMode.Launcher) {
super.onNewIntent(intent)
val navContract = intent?.let { GestureNavContract.fromIntent(it) }
if (navContract != null) {
homeTransitionManager.resolve(navContract)
homeTransitionManager.resolve(navContract, window)
} else {
homeTransitionManager.clear()
onBackPressed()
}
}

View File

@ -1,11 +1,9 @@
package de.mm20.launcher2.ui.launcher
import android.app.WallpaperManager
import android.content.Intent
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.background
@ -16,34 +14,40 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import com.android.launcher3.GestureNavContract
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.assistant.AssistantScaffold
import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.base.ProvideCurrentTime
import de.mm20.launcher2.ui.base.ProvideSettings
import de.mm20.launcher2.ui.component.NavBarEffects
import de.mm20.launcher2.ui.ktx.animateTo
import de.mm20.launcher2.ui.launcher.transitions.HomeTransition
import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionManager
import de.mm20.launcher2.ui.launcher.transitions.LocalHomeTransitionManager
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
import de.mm20.launcher2.ui.locals.LocalWindowSize
import de.mm20.launcher2.ui.theme.LauncherTheme
import kotlin.math.pow
abstract class SharedLauncherActivity(
@ -86,15 +90,22 @@ abstract class SharedLauncherActivity(
val systemUiController = rememberSystemUiController()
val enterTransition = remember { mutableStateOf(1f) }
val enterTransitionProgress = remember { mutableStateOf(1f) }
var enterTransition by remember {
mutableStateOf<HomeTransition?>(
null
)
}
LaunchedEffect(null) {
homeTransitionManager
.currentTransition
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.collect {
enterTransition.value = 0f
enterTransition.animateTo(1f)
enterTransitionProgress.value = 0f
enterTransition = it
enterTransitionProgress.animateTo(1f)
enterTransition = null
}
}
@ -138,29 +149,33 @@ abstract class SharedLauncherActivity(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
scaleX = 0.5f + enterTransition.value * 0.5f
scaleY = 0.5f + enterTransition.value * 0.5f
alpha = enterTransition.value
scaleX =
0.5f + enterTransitionProgress.value * 0.5f
scaleY =
0.5f + enterTransitionProgress.value * 0.5f
alpha = enterTransitionProgress.value
},
darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav,
)
}
Settings.AppearanceSettings.Layout.Pager,
Settings.AppearanceSettings.Layout.PagerReversed -> {
PagerScaffold(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
scaleX = enterTransition.value
scaleY = enterTransition.value
alpha = enterTransition.value
scaleX = enterTransitionProgress.value
scaleY = enterTransitionProgress.value
alpha = enterTransitionProgress.value
},
darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav,
reverse = layout == Settings.AppearanceSettings.Layout.PagerReversed
)
}
else -> {}
}
}
@ -170,6 +185,25 @@ abstract class SharedLauncherActivity(
.navigationBarsPadding()
.imePadding()
)
enterTransition?.let {
val dX = it.startBounds.center.x - it.targetBounds.center.x
val dY = it.startBounds.center.y - it.targetBounds.center.y
val s = (it.startBounds.minDimension / it.targetBounds.minDimension - 1f) * 0.5f
Box(
modifier = Modifier
.align(Alignment.TopStart)
.graphicsLayer {
val p = (enterTransitionProgress.value).pow(2f)
transformOrigin = TransformOrigin.Center
translationX = it.targetBounds.left + dX * (1 - p)
translationY = it.targetBounds.top + dY * (1 - p)
alpha = enterTransitionProgress.value
scaleX = 1f + s * (1 - p)
scaleY = 1f + s * (1 - p)
}) {
it.icon?.invoke(Offset(dX, dY)) { enterTransitionProgress.value }
}
}
}
}
}

View File

@ -73,7 +73,11 @@ fun GridItem(modifier: Modifier = Modifier, item: Searchable, showLabels: Boolea
bounds.right > 0f && bounds.left < windowSize.width &&
bounds.bottom > 0f && bounds.top < windowSize.height
) {
return@HandleHomeTransition HomeTransitionParams(bounds)
return@HandleHomeTransition HomeTransitionParams(
bounds
) { _, _ ->
ShapedLauncherIcon(size = LocalGridIconSize.current, icon = { icon })
}
}
return@HandleHomeTransition null
}

View File

@ -0,0 +1,11 @@
package de.mm20.launcher2.ui.launcher.transitions
import androidx.compose.runtime.Composable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
data class HomeTransition(
val startBounds: Rect,
val targetBounds: Rect,
val icon: (@Composable (animVector: Offset, progress: () -> Float) -> Unit)? = null
)

View File

@ -1,28 +1,42 @@
package de.mm20.launcher2.ui.launcher.transitions
import android.view.Window
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.toAndroidRectF
import com.android.launcher3.GestureNavContract
import kotlinx.coroutines.flow.MutableSharedFlow
class HomeTransitionManager {
val currentTransition = MutableSharedFlow<HomeTransitionParams?>(1)
val currentTransition = MutableSharedFlow<HomeTransition?>(1)
private val handlers = mutableSetOf<HomeTransitionHandler>()
fun resolve(gestureNavContract: GestureNavContract) {
fun resolve(gestureNavContract: GestureNavContract, window: Window) {
for (handler in handlers) {
val result = handler.handle(gestureNavContract)
if (result != null) {
gestureNavContract.sendEndPosition(result.targetBounds.toAndroidRectF())
currentTransition.tryEmit(result)
val startRect = Rect(Offset(0f, 0f), Size(window.decorView.width.toFloat(), window.decorView.height.toFloat()))
val targetBounds = result.targetBounds
gestureNavContract.sendEndPosition(targetBounds.toAndroidRectF())
currentTransition.tryEmit(HomeTransition(
startBounds = startRect,
icon = result.icon,
targetBounds = targetBounds,
))
return
}
}
currentTransition.tryEmit(null)
}
fun clear() {
currentTransition.tryEmit(null)
}
fun registerHandler(handler: HomeTransitionHandler) {
handlers.add(handler)
}

View File

@ -1,8 +0,0 @@
package de.mm20.launcher2.ui.launcher.transitions
import androidx.compose.runtime.Composable
import androidx.compose.ui.geometry.Rect
fun interface HomeTransitionOffer {
fun accept(targetBounds: Rect, icon: @Composable () -> Unit)
}

View File

@ -2,10 +2,11 @@ package de.mm20.launcher2.ui.launcher.transitions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
@Stable
data class HomeTransitionParams(
val targetBounds: Rect,
val icon: (@Composable () -> Unit)? = null
val icon: (@Composable (animVector: Offset, progress: () -> Float) -> Unit)? = null
)