(fix) home transition to target that is no longer in view
This commit is contained in:
parent
8f4766d28a
commit
46d7293175
@ -2,6 +2,7 @@ package de.mm20.launcher2.ui.launcher
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.android.launcher3.GestureNavContract
|
import com.android.launcher3.GestureNavContract
|
||||||
|
import de.mm20.launcher2.ui.launcher.scaffold.ScaffoldPage
|
||||||
|
|
||||||
|
|
||||||
class LauncherActivity: SharedLauncherActivity(LauncherActivityMode.Launcher) {
|
class LauncherActivity: SharedLauncherActivity(LauncherActivityMode.Launcher) {
|
||||||
@ -9,7 +10,12 @@ class LauncherActivity: SharedLauncherActivity(LauncherActivityMode.Launcher) {
|
|||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
val navContract = intent.let { GestureNavContract.fromIntent(it) }
|
val navContract = intent.let { GestureNavContract.fromIntent(it) }
|
||||||
if (navContract != null) {
|
if (navContract != null) {
|
||||||
enterHomeTransitionManager.resolve(navContract, window)
|
val page = if (System.currentTimeMillis() - pauseTime > 5000L || pauseOnHome) {
|
||||||
|
ScaffoldPage.Home
|
||||||
|
} else {
|
||||||
|
ScaffoldPage.Secondary
|
||||||
|
}
|
||||||
|
enterHomeTransitionManager.resolve(navContract, window, page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -432,6 +432,12 @@ abstract class SharedLauncherActivity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pauseTime = 0L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the scaffold was on home screen when the activity was paused.
|
||||||
|
*/
|
||||||
|
var pauseOnHome = false
|
||||||
var isNewIntent = false
|
var isNewIntent = false
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
|
|||||||
@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.absoluteOffset
|
|||||||
import androidx.compose.foundation.layout.add
|
import androidx.compose.foundation.layout.add
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.displayCutout
|
import androidx.compose.foundation.layout.displayCutout
|
||||||
import androidx.compose.foundation.layout.exclude
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.ime
|
import androidx.compose.foundation.layout.ime
|
||||||
@ -39,7 +38,6 @@ import androidx.compose.foundation.layout.navigationBars
|
|||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.offset
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
|
||||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.foundation.layout.statusBarsPadding
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
@ -49,6 +47,7 @@ import androidx.compose.foundation.shape.CutCornerShape
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
@ -245,14 +244,16 @@ internal class LauncherScaffoldState(
|
|||||||
initialIsLocked: Boolean = false,
|
initialIsLocked: Boolean = false,
|
||||||
initialIsSearchBarHidden: Boolean = false,
|
initialIsSearchBarHidden: Boolean = false,
|
||||||
) {
|
) {
|
||||||
var currentOffset by mutableStateOf(when {
|
var currentOffset by mutableStateOf(
|
||||||
|
when {
|
||||||
initialGesture == null || initialGesture.orientation == null || config[initialGesture]?.animation == ScaffoldAnimation.Rubberband -> Offset.Zero
|
initialGesture == null || initialGesture.orientation == null || config[initialGesture]?.animation == ScaffoldAnimation.Rubberband -> Offset.Zero
|
||||||
initialGesture == Gesture.SwipeRight -> Offset(-size.width, 0f)
|
initialGesture == Gesture.SwipeRight -> Offset(-size.width, 0f)
|
||||||
initialGesture == Gesture.SwipeLeft -> Offset(size.width, 0f)
|
initialGesture == Gesture.SwipeLeft -> Offset(size.width, 0f)
|
||||||
initialGesture == Gesture.SwipeUp -> Offset(0f, -size.height)
|
initialGesture == Gesture.SwipeUp -> Offset(0f, -size.height)
|
||||||
initialGesture == Gesture.SwipeDown -> Offset(0f, size.height)
|
initialGesture == Gesture.SwipeDown -> Offset(0f, size.height)
|
||||||
else -> Offset.Zero
|
else -> Offset.Zero
|
||||||
})
|
}
|
||||||
|
)
|
||||||
private set
|
private set
|
||||||
var currentZOffset by mutableFloatStateOf(
|
var currentZOffset by mutableFloatStateOf(
|
||||||
if (initialGesture != null && initialGesture.orientation == null) 1f else 0f
|
if (initialGesture != null && initialGesture.orientation == null) 1f else 0f
|
||||||
@ -1211,10 +1212,11 @@ internal fun LauncherScaffold(
|
|||||||
config.homeComponent.onPreActivate(state)
|
config.homeComponent.onPreActivate(state)
|
||||||
config.homeComponent.onActivate(state)
|
config.homeComponent.onActivate(state)
|
||||||
|
|
||||||
var pauseTime = 0L
|
val activity = (activity as? SharedLauncherActivity) ?: return@LaunchedEffect
|
||||||
|
|
||||||
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
try {
|
try {
|
||||||
if (pauseTime > 0L && System.currentTimeMillis() - pauseTime < 50L && (activity as? SharedLauncherActivity)?.isNewIntent == true) {
|
if (activity.pauseTime > 0L && System.currentTimeMillis() - activity.pauseTime < 50L && activity.isNewIntent) {
|
||||||
if (!state.isLocked) {
|
if (!state.isLocked) {
|
||||||
if (state.currentProgress > 0f) {
|
if (state.currentProgress > 0f) {
|
||||||
state.onPredictiveBackEnd()
|
state.onPredictiveBackEnd()
|
||||||
@ -1224,7 +1226,7 @@ internal fun LauncherScaffold(
|
|||||||
} else {
|
} else {
|
||||||
activity.onBackPressedDispatcher.onBackPressed()
|
activity.onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
} else if (pauseTime > 0L && System.currentTimeMillis() - pauseTime > 5000L) {
|
} else if (activity.pauseTime > 0L && System.currentTimeMillis() - activity.pauseTime > 5000L) {
|
||||||
if (!state.isLocked) {
|
if (!state.isLocked) {
|
||||||
state.reset()
|
state.reset()
|
||||||
searchVM.reset()
|
searchVM.reset()
|
||||||
@ -1232,7 +1234,8 @@ internal fun LauncherScaffold(
|
|||||||
}
|
}
|
||||||
awaitCancellation()
|
awaitCancellation()
|
||||||
} finally {
|
} finally {
|
||||||
pauseTime = System.currentTimeMillis()
|
activity.pauseTime = System.currentTimeMillis()
|
||||||
|
activity.pauseOnHome = !state.isSettledOnSecondaryPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1260,8 +1263,10 @@ internal fun LauncherScaffold(
|
|||||||
|
|
||||||
if (config.wallpaperBlurRadius > 0.dp) {
|
if (config.wallpaperBlurRadius > 0.dp) {
|
||||||
val wallpaperBlur by animateIntAsState(
|
val wallpaperBlur by animateIntAsState(
|
||||||
if (state.currentProgress >= 0.5f && (state.currentComponent?.drawBackground ?: config.homeComponent.drawBackground)
|
if (state.currentProgress >= 0.5f && (state.currentComponent?.drawBackground
|
||||||
|| state.currentProgress < 0.5f && config.homeComponent.drawBackground) {
|
?: config.homeComponent.drawBackground)
|
||||||
|
|| state.currentProgress < 0.5f && config.homeComponent.drawBackground
|
||||||
|
) {
|
||||||
8.dp.toPixels().toInt()
|
8.dp.toPixels().toInt()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
@ -1412,6 +1417,9 @@ internal fun LauncherScaffold(
|
|||||||
bottom = filterBarHeight
|
bottom = filterBarHeight
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalScaffoldPage provides ScaffoldPage.Home,
|
||||||
|
) {
|
||||||
config.homeComponent.Component(
|
config.homeComponent.Component(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@ -1446,6 +1454,7 @@ internal fun LauncherScaffold(
|
|||||||
.asPaddingValues(),
|
.asPaddingValues(),
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
SecondaryPage(
|
SecondaryPage(
|
||||||
state = state,
|
state = state,
|
||||||
@ -1566,6 +1575,7 @@ private fun SecondaryPage(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
insets: PaddingValues,
|
insets: PaddingValues,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val components = remember(config) {
|
val components = remember(config) {
|
||||||
setOfNotNull(
|
setOfNotNull(
|
||||||
config.swipeUp?.component,
|
config.swipeUp?.component,
|
||||||
@ -1602,8 +1612,12 @@ private fun SecondaryPage(
|
|||||||
)
|
)
|
||||||
val composable = composables[component]
|
val composable = composables[component]
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalScaffoldPage provides ScaffoldPage.Secondary
|
||||||
|
) {
|
||||||
composable?.invoke(mod, insets, state)
|
composable?.invoke(mod, insets, state)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keep other components alive, but out of the viewport
|
// Keep other components alive, but out of the viewport
|
||||||
Box(
|
Box(
|
||||||
@ -1616,7 +1630,6 @@ private fun SecondaryPage(
|
|||||||
v.invoke(modifier, insets, state)
|
v.invoke(modifier, insets, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.homePageAnimation(
|
private fun Modifier.homePageAnimation(
|
||||||
@ -1657,7 +1670,11 @@ private fun Modifier.homePageAnimation(
|
|||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this then component.homePageModifier(state, Modifier.background(backgroundColor).absoluteOffset {
|
return this then component.homePageModifier(
|
||||||
|
state,
|
||||||
|
Modifier
|
||||||
|
.background(backgroundColor)
|
||||||
|
.absoluteOffset {
|
||||||
IntOffset(
|
IntOffset(
|
||||||
x = if (dir.orientation == Orientation.Horizontal) state.currentOffset.x.toInt() else 0,
|
x = if (dir.orientation == Orientation.Horizontal) state.currentOffset.x.toInt() else 0,
|
||||||
y = if (dir.orientation == Orientation.Vertical) state.currentOffset.y.toInt() else 0
|
y = if (dir.orientation == Orientation.Vertical) state.currentOffset.y.toInt() else 0
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.scaffold
|
||||||
|
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
|
||||||
|
enum class ScaffoldPage {
|
||||||
|
Home,
|
||||||
|
Secondary,
|
||||||
|
}
|
||||||
|
|
||||||
|
val LocalScaffoldPage = compositionLocalOf<ScaffoldPage?> { null }
|
||||||
@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.launcher.transitions
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import com.android.launcher3.GestureNavContract
|
import com.android.launcher3.GestureNavContract
|
||||||
|
import de.mm20.launcher2.ui.launcher.scaffold.LocalScaffoldPage
|
||||||
|
|
||||||
fun interface EnterHomeTransitionHandler {
|
fun interface EnterHomeTransitionHandler {
|
||||||
fun handle(gestureNavContract: GestureNavContract): EnterHomeTransitionParams?
|
fun handle(gestureNavContract: GestureNavContract): EnterHomeTransitionParams?
|
||||||
@ -11,11 +12,14 @@ fun interface EnterHomeTransitionHandler {
|
|||||||
@Composable
|
@Composable
|
||||||
fun HandleEnterHomeTransition(handler: EnterHomeTransitionHandler) {
|
fun HandleEnterHomeTransition(handler: EnterHomeTransitionHandler) {
|
||||||
val transitionManager = LocalEnterHomeTransitionManager.current
|
val transitionManager = LocalEnterHomeTransitionManager.current
|
||||||
DisposableEffect(handler) {
|
val page = LocalScaffoldPage.current
|
||||||
transitionManager?.registerHandler(handler)
|
if (page != null && transitionManager != null) {
|
||||||
|
DisposableEffect(handler, page) {
|
||||||
|
transitionManager.registerHandler(handler, page)
|
||||||
|
|
||||||
onDispose {
|
onDispose {
|
||||||
transitionManager?.unregisterHandler(handler)
|
transitionManager.unregisterHandler(handler, page)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,21 +3,24 @@ package de.mm20.launcher2.ui.launcher.transitions
|
|||||||
import android.view.Window
|
import android.view.Window
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.graphics.toAndroidRect
|
import androidx.compose.ui.graphics.toAndroidRect
|
||||||
import androidx.compose.ui.graphics.toAndroidRectF
|
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.IntRect
|
import androidx.compose.ui.unit.IntRect
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.core.graphics.toRectF
|
import androidx.core.graphics.toRectF
|
||||||
import com.android.launcher3.GestureNavContract
|
import com.android.launcher3.GestureNavContract
|
||||||
|
import de.mm20.launcher2.ui.launcher.scaffold.ScaffoldPage
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
|
||||||
class EnterHomeTransitionManager {
|
class EnterHomeTransitionManager {
|
||||||
|
|
||||||
val currentTransition = MutableSharedFlow<EnterHomeTransition?>(1)
|
val currentTransition = MutableSharedFlow<EnterHomeTransition?>(1)
|
||||||
|
|
||||||
private val handlers = mutableSetOf<EnterHomeTransitionHandler>()
|
private val homeHandlers = mutableSetOf<EnterHomeTransitionHandler>()
|
||||||
|
private val secondaryHandlers = mutableSetOf<EnterHomeTransitionHandler>()
|
||||||
|
|
||||||
|
fun resolve(gestureNavContract: GestureNavContract, window: Window, page: ScaffoldPage) {
|
||||||
|
val handlers = if (page === ScaffoldPage.Secondary) secondaryHandlers else homeHandlers
|
||||||
|
|
||||||
fun resolve(gestureNavContract: GestureNavContract, window: Window) {
|
|
||||||
for (handler in handlers) {
|
for (handler in handlers) {
|
||||||
val result = handler.handle(gestureNavContract)
|
val result = handler.handle(gestureNavContract)
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
@ -44,12 +47,23 @@ class EnterHomeTransitionManager {
|
|||||||
currentTransition.tryEmit(null)
|
currentTransition.tryEmit(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerHandler(handler: EnterHomeTransitionHandler) {
|
/**
|
||||||
handlers.add(handler)
|
* The scaffold page that needs to be active for this handler to be considered.
|
||||||
|
*/
|
||||||
|
fun registerHandler(handler: EnterHomeTransitionHandler, page: ScaffoldPage) {
|
||||||
|
if (page == ScaffoldPage.Home) {
|
||||||
|
homeHandlers.add(handler)
|
||||||
|
} else if (page == ScaffoldPage.Secondary) {
|
||||||
|
secondaryHandlers.add(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unregisterHandler(handler: EnterHomeTransitionHandler) {
|
fun unregisterHandler(handler: EnterHomeTransitionHandler, page: ScaffoldPage) {
|
||||||
handlers.remove(handler)
|
if (page == ScaffoldPage.Home) {
|
||||||
|
homeHandlers.remove(handler)
|
||||||
|
} else if (page == ScaffoldPage.Secondary) {
|
||||||
|
secondaryHandlers.remove(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user