Animate closing windows into their icons
This commit is contained in:
parent
9f93f0c750
commit
6908f94417
@ -36,7 +36,7 @@ class GestureNavContract(
|
|||||||
* Sends the position information to the receiver
|
* Sends the position information to the receiver
|
||||||
*/
|
*/
|
||||||
@TargetApi(Build.VERSION_CODES.R)
|
@TargetApi(Build.VERSION_CODES.R)
|
||||||
fun sendEndPosition(position: RectF?, surfaceControl: SurfaceControl?) {
|
fun sendEndPosition(position: RectF? = null, surfaceControl: SurfaceControl? = null) {
|
||||||
val result = Bundle()
|
val result = Bundle()
|
||||||
result.putParcelable(EXTRA_ICON_POSITION, position)
|
result.putParcelable(EXTRA_ICON_POSITION, position)
|
||||||
result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl)
|
result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package de.mm20.launcher2.ui.launcher
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
@ -16,6 +17,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
@ -35,6 +37,7 @@ import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView
|
|||||||
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsSheet
|
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsSheet
|
||||||
import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionManager
|
import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionManager
|
||||||
import de.mm20.launcher2.ui.launcher.transitions.LocalHomeTransitionManager
|
import de.mm20.launcher2.ui.launcher.transitions.LocalHomeTransitionManager
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
||||||
import de.mm20.launcher2.ui.theme.LauncherTheme
|
import de.mm20.launcher2.ui.theme.LauncherTheme
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
@ -48,13 +51,18 @@ class LauncherActivity : BaseActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val windowSize = Resources.getSystem().displayMetrics.let {
|
||||||
|
Size(it.widthPixels.toFloat(), it.heightPixels.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
viewModel.setDarkMode(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
|
viewModel.setDarkMode(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalHomeTransitionManager provides homeTransitionManager
|
LocalHomeTransitionManager provides homeTransitionManager,
|
||||||
|
LocalWindowSize provides windowSize
|
||||||
) {
|
) {
|
||||||
LauncherTheme {
|
LauncherTheme {
|
||||||
ProvideSettings {
|
ProvideSettings {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search.common
|
package de.mm20.launcher2.ui.launcher.search.common.grid
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@ -29,19 +29,19 @@ import de.mm20.launcher2.ui.ktx.toDp
|
|||||||
import de.mm20.launcher2.ui.ktx.toPixels
|
import de.mm20.launcher2.ui.ktx.toPixels
|
||||||
import de.mm20.launcher2.ui.launcher.search.apps.AppItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.apps.AppItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.grid.GridItemVM
|
|
||||||
import de.mm20.launcher2.ui.launcher.search.contacts.ContactItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.contacts.ContactItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.files.FileItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.files.FileItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.shortcut.ShortcutItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.shortcut.ShortcutItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.website.WebsiteItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.website.WebsiteItemGridPopup
|
||||||
import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaItemGridPopup
|
import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaItemGridPopup
|
||||||
|
import de.mm20.launcher2.ui.launcher.transitions.HandleHomeTransition
|
||||||
|
import de.mm20.launcher2.ui.launcher.transitions.HomeTransitionParams
|
||||||
import de.mm20.launcher2.ui.locals.LocalGridIconSize
|
import de.mm20.launcher2.ui.locals.LocalGridIconSize
|
||||||
import de.mm20.launcher2.ui.locals.LocalWindowPosition
|
import de.mm20.launcher2.ui.locals.LocalWindowPosition
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GridItem(modifier: Modifier = Modifier, item: Searchable, showLabels: Boolean = true) {
|
fun GridItem(modifier: Modifier = Modifier, item: Searchable, showLabels: Boolean = true) {
|
||||||
val viewModel = remember(item.key) { GridItemVM(item) }
|
val viewModel = remember(item.key) { GridItemVM(item) }
|
||||||
@ -58,6 +58,22 @@ fun GridItem(modifier: Modifier = Modifier, item: Searchable, showLabels: Boolea
|
|||||||
val launchOnPress =
|
val launchOnPress =
|
||||||
item is File || item is Application || item is AppShortcut || item is Website || item is Wikipedia
|
item is File || item is Application || item is AppShortcut || item is Website || item is Wikipedia
|
||||||
|
|
||||||
|
val windowSize = LocalWindowSize.current
|
||||||
|
|
||||||
|
if (item is Application) {
|
||||||
|
HandleHomeTransition {
|
||||||
|
val cn = ComponentName(item.`package`, item.activity)
|
||||||
|
if (
|
||||||
|
it.componentName == cn &&
|
||||||
|
bounds.right > 0f && bounds.left < windowSize.width &&
|
||||||
|
bounds.bottom > 0f && bounds.top < windowSize.height
|
||||||
|
) {
|
||||||
|
return@HandleHomeTransition HomeTransitionParams(bounds)
|
||||||
|
}
|
||||||
|
return@HandleHomeTransition null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShapedLauncherIcon(
|
ShapedLauncherIcon(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned {
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
import de.mm20.launcher2.search.data.Searchable
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.GridItem
|
|
||||||
import de.mm20.launcher2.ui.layout.BottomReversed
|
import de.mm20.launcher2.ui.layout.BottomReversed
|
||||||
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.transitions
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import com.android.launcher3.GestureNavContract
|
||||||
|
|
||||||
|
fun interface HomeTransitionHandler {
|
||||||
|
fun handle(gestureNavContract: GestureNavContract): HomeTransitionParams?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HandleHomeTransition(handler: HomeTransitionHandler) {
|
||||||
|
val transitionManager = LocalHomeTransitionManager.current
|
||||||
|
DisposableEffect(null) {
|
||||||
|
transitionManager?.registerHandler(handler)
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
transitionManager?.unregisterHandler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.transitions
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
|
import androidx.compose.ui.graphics.toAndroidRectF
|
||||||
|
import com.android.launcher3.GestureNavContract
|
||||||
|
|
||||||
|
class HomeTransitionManager {
|
||||||
|
|
||||||
|
private val handlers = mutableSetOf<HomeTransitionHandler>()
|
||||||
|
|
||||||
|
fun resolve(gestureNavContract: GestureNavContract) {
|
||||||
|
for (handler in handlers) {
|
||||||
|
val result = handler.handle(gestureNavContract)
|
||||||
|
if (result != null) {
|
||||||
|
gestureNavContract.sendEndPosition(result.targetBounds.toAndroidRectF())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dispatch(params: HomeTransitionParams) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerHandler(handler: HomeTransitionHandler) {
|
||||||
|
handlers.add(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregisterHandler(handler: HomeTransitionHandler) {
|
||||||
|
handlers.remove(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val LocalHomeTransitionManager = compositionLocalOf<HomeTransitionManager?> { null }
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.transitions
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
|
|
||||||
|
data class HomeTransitionParams(
|
||||||
|
val targetBounds: Rect,
|
||||||
|
val icon: (@Composable () -> Unit)? = null
|
||||||
|
)
|
||||||
Loading…
x
Reference in New Issue
Block a user