diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index bc62c30b..0538f8a4 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -14,7 +14,7 @@ androidx-lifecycle = "2.10.0" # Used by the Wear OS app only
androidx-wear-compose = "1.6.0"
apollo = "4.4.2"
apollo-adapters = "0.7.0"
-apollo-cache = "1.0.0"
+apollo-cache = "1.0.1"
coil = "3.4.0"
compose = "1.10.6" # Used by the Wear OS app only
compose-material-icons-extended = "1.7.8"
@@ -28,7 +28,7 @@ jetbrains-compose = "1.11.0-alpha04"
jetbrains-lifecycle = "2.10.0"
jetbrains-material3-adaptive-nav3 = "1.3.0-alpha06"
jetbrains-compose-material-icons-extended = "1.7.3"
-nav3-ui = "1.1.0-alpha04"
+nav3-ui = "1.1.0-beta01"
junit = "4.13.2"
koin = "4.1.1"
kotlin = "2.3.20"
diff --git a/iosApp/Android Makers.xcodeproj/project.pbxproj b/iosApp/Android Makers.xcodeproj/project.pbxproj
index 2bf94261..973c2174 100644
--- a/iosApp/Android Makers.xcodeproj/project.pbxproj
+++ b/iosApp/Android Makers.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
61C8A7672D8B199B0046C2CC /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 61C8A7662D8B199B0046C2CC /* FirebaseCore */; };
61C8A7692D8B199B0046C2CC /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 61C8A7682D8B199B0046C2CC /* FirebaseFirestore */; };
61C8A76B2D8B1C490046C2CC /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 61C8A76A2D8B1C490046C2CC /* FirebaseAuth */; };
+ 61C8A76D2D8B1C490046C2CC /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 61C8A76C2D8B1C490046C2CC /* FirebaseMessaging */; };
B2CD272F234D06530016AA02 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CD272E234D06530016AA02 /* AppDelegate.swift */; };
B2CD2731234D06530016AA02 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CD2730234D06530016AA02 /* SceneDelegate.swift */; };
B2CD2733234D06530016AA02 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CD2732234D06530016AA02 /* ContentView.swift */; };
@@ -40,6 +41,7 @@
61C8A7672D8B199B0046C2CC /* FirebaseCore in Frameworks */,
61C8A76B2D8B1C490046C2CC /* FirebaseAuth in Frameworks */,
61C8A7692D8B199B0046C2CC /* FirebaseFirestore in Frameworks */,
+ 61C8A76D2D8B1C490046C2CC /* FirebaseMessaging in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -131,6 +133,7 @@
61C8A7662D8B199B0046C2CC /* FirebaseCore */,
61C8A7682D8B199B0046C2CC /* FirebaseFirestore */,
61C8A76A2D8B1C490046C2CC /* FirebaseAuth */,
+ 61C8A76C2D8B1C490046C2CC /* FirebaseMessaging */,
);
productName = RobotConf;
productReference = B2CD272B234D06530016AA02 /* Android Makers.app */;
@@ -205,7 +208,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "cd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
+ shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode";
};
/* End PBXShellScriptBuildPhase section */
@@ -480,6 +483,11 @@
package = 61C8A7632D8B199B0046C2CC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseAuth;
};
+ 61C8A76C2D8B1C490046C2CC /* FirebaseMessaging */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 61C8A7632D8B199B0046C2CC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseMessaging;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = B2CD2723234D06530016AA02 /* Project object */;
diff --git a/shared/ui/src/commonMain/composeResources/values/strings.xml b/shared/ui/src/commonMain/composeResources/values/strings.xml
index aa018c11..fc875354 100644
--- a/shared/ui/src/commonMain/composeResources/values/strings.xml
+++ b/shared/ui/src/commonMain/composeResources/values/strings.xml
@@ -49,7 +49,7 @@
Expert
- Android Makers by droidcon is a two days event held in Paris on April 9th and 10th 2025. Join us in tackling the present and future of Android with the hottest experts of the domain. There will be technical sessions, workshops and opportunities to meet your favorites speakers. All the Talks will be recorded, and uploaded on the Youtube channel.
+ Android Makers by droidcon is a two days event held in Paris on April 9th and 10th 2026. Join us in tackling the present and future of Android with the hottest experts of the domain. There will be technical sessions, workshops and opportunities to meet your favorites speakers. All the Talks will be recorded, and uploaded on the Youtube channel.
Open the slides
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/MainLayout.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/MainLayout.kt
index 7b315a0c..2f77b3f3 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/MainLayout.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/MainLayout.kt
@@ -100,7 +100,7 @@ fun MainLayout(
)
}
- } // AndroidMakersTheme
+ }
}
@Composable
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
index e8f230e3..f3ca4fa1 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/common/navigation/AVALayout.kt
@@ -1,11 +1,18 @@
package com.androidmakers.ui.common.navigation
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -16,6 +23,7 @@ import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
@@ -297,7 +305,13 @@ private fun AVANavDisplay(
val entryProvider = entryProvider {
// Tab entries
entry {
- FeedScreen()
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ FeedScreen()
+ }
}
entry(
@@ -305,32 +319,56 @@ private fun AVANavDisplay(
detailPlaceholder = {}
)
) {
- AgendaLayout(
- showFilterBottomSheet = showAgendaFilterBottomSheet,
- onFilterBottomSheetDismiss = onDismissAgendaFilter,
- onSessionClick = { sessionId -> navigator.navigateToSessionDetail(sessionId) }
- )
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ AgendaLayout(
+ showFilterBottomSheet = showAgendaFilterBottomSheet,
+ onFilterBottomSheetDismiss = onDismissAgendaFilter,
+ onSessionClick = { sessionId -> navigator.navigateToSessionDetail(sessionId) }
+ )
+ }
}
entry {
- SpeakerScreen(
- viewModel = koinViewModel(),
- navigateToSpeakerDetails = { speakerId -> navigator.navigate(SpeakerDetailKey(speakerId)) },
- sharedTransitionScope = sharedTransitionScope,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- )
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ SpeakerScreen(
+ viewModel = koinViewModel(),
+ navigateToSpeakerDetails = { speakerId -> navigator.navigate(SpeakerDetailKey(speakerId)) },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ )
+ }
}
entry {
- SponsorsScreen()
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ SponsorsScreen()
+ }
}
entry {
- InfoScreen(
- versionCode = versionCode,
- versionName = versionName,
- featureFlags = featureFlags,
- )
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ InfoScreen(
+ versionCode = versionCode,
+ versionName = versionName,
+ featureFlags = featureFlags,
+ )
+ }
}
// Detail entries
@@ -341,41 +379,61 @@ private fun AVANavDisplay(
BottomSheetSceneStrategy.bottomSheet()
}
) { key ->
- SessionDetailScreen(
- viewModel = koinViewModel(key = key.sessionId) { parametersOf(key.sessionId) },
- onBackClick = { navigator.goBack() },
- onSpeakerClick = { speakerId -> navigator.navigate(SpeakerDetailKey(speakerId)) },
- showBackButton = isWideScreen,
- showTopBar = isWideScreen,
- sharedTransitionScope = sharedTransitionScope,
- // LocalNavAnimatedContentScope is unavailable in OverlayScene (bottom sheet)
- animatedVisibilityScope = if (isWideScreen) {
- LocalNavAnimatedContentScope.current
- } else {
- null
- },
- )
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ SessionDetailScreen(
+ viewModel = koinViewModel(key = key.sessionId) { parametersOf(key.sessionId) },
+ onBackClick = { navigator.goBack() },
+ onSpeakerClick = { speakerId -> navigator.navigate(SpeakerDetailKey(speakerId)) },
+ showBackButton = isWideScreen,
+ showTopBar = isWideScreen,
+ sharedTransitionScope = sharedTransitionScope,
+ // LocalNavAnimatedContentScope is unavailable in OverlayScene (bottom sheet)
+ animatedVisibilityScope = if (isWideScreen) {
+ LocalNavAnimatedContentScope.current
+ } else {
+ null
+ },
+ )
+ }
}
entry { key ->
- SpeakerDetailsRoute(
- speakerDetailsViewModel = koinViewModel(key = key.speakerId) { parametersOf(key.speakerId) },
- onBackClick = { navigator.goBack() },
- sharedTransitionScope = sharedTransitionScope,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- )
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ ) {
+ SpeakerDetailsRoute(
+ speakerDetailsViewModel = koinViewModel(key = key.speakerId) { parametersOf(key.speakerId) },
+ onBackClick = { navigator.goBack() },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ )
+ }
}
}
val bottomSheetStrategy = remember { BottomSheetSceneStrategy() }
val listDetailStrategy = rememberListDetailSceneStrategy()
+
NavDisplay(
entries = navigationState.toDecoratedEntries(entryProvider),
sceneStrategies = listOf(bottomSheetStrategy, listDetailStrategy),
- onBack = { navigator.goBack() }
+ onBack = navigator::goBack,
+ transitionSpec = {
+ fadeIn(tween(200)) togetherWith ExitTransition.None
+ },
+ popTransitionSpec = {
+ EnterTransition.None togetherWith fadeOut(tween(200))
+ },
)
}
+
}
@Composable
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/feed/FeedScreen.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/feed/FeedScreen.kt
index b63cc289..c12f110a 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/feed/FeedScreen.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/feed/FeedScreen.kt
@@ -18,7 +18,10 @@ fun FeedScreen() {
val lce by viewModel.values.collectAsStateWithLifecycle()
val dismissedAlertIds by viewModel.dismissedAlertIds.collectAsStateWithLifecycle()
- LceLayout(lce = lce, onRetry = { viewModel.refresh() }) { feedItems ->
+ LceLayout(
+ lce = lce,
+ onRetry = { viewModel.refresh() }
+ ) { feedItems ->
val visibleItems = feedItems.filter { item ->
item !is FeedItem.Alert || item.id !in dismissedAlertIds
}
diff --git a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerListScreen.kt b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerListScreen.kt
index 843c76b6..14351c58 100644
--- a/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerListScreen.kt
+++ b/shared/ui/src/commonMain/kotlin/com/androidmakers/ui/speakers/SpeakerListScreen.kt
@@ -50,10 +50,10 @@ import com.androidmakers.ui.theme.LocalIsNeobrutalism
import com.androidmakers.ui.theme.neoBrutalElevation
import fr.androidmakers.domain.model.Speaker
import fr.paug.androidmakers.ui.Res
+import fr.paug.androidmakers.ui.back
import fr.paug.androidmakers.ui.ic_arrow_back
import fr.paug.androidmakers.ui.ic_clear
import fr.paug.androidmakers.ui.ic_search
-import fr.paug.androidmakers.ui.back
import fr.paug.androidmakers.ui.speaker_search_placeholder
import fr.paug.androidmakers.ui.speakers
import org.jetbrains.compose.resources.painterResource
@@ -62,10 +62,10 @@ import org.jetbrains.compose.resources.stringResource
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SpeakerScreen(
- viewModel: SpeakerListViewModel,
- navigateToSpeakerDetails: (String) -> Unit,
- sharedTransitionScope: SharedTransitionScope? = null,
- animatedVisibilityScope: AnimatedVisibilityScope? = null,
+ viewModel: SpeakerListViewModel,
+ navigateToSpeakerDetails: (String) -> Unit,
+ sharedTransitionScope: SharedTransitionScope? = null,
+ animatedVisibilityScope: AnimatedVisibilityScope? = null,
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
@@ -81,7 +81,7 @@ fun SpeakerScreen(
val speakers = (state as Lce.Content).content.speakers
val filteredSpeakers = remember(speakers, text) {
- speakers.filter { it.name?.contains(text, ignoreCase = true) == true }
+ speakers.filter { it.name?.contains(text, ignoreCase = true) == true }
}
var searchHeight by remember { mutableStateOf(56.dp) }
@@ -112,7 +112,6 @@ fun SpeakerScreen(
}
}
-
}
@Suppress("LongParameterList")
@@ -232,11 +231,11 @@ private fun SpeakerListContent(
@Composable
fun SpeakerItem(
- speaker: Speaker,
- modifier: Modifier = Modifier,
- navigateToSpeakerDetails: (String) -> Unit,
- sharedTransitionScope: SharedTransitionScope? = null,
- animatedVisibilityScope: AnimatedVisibilityScope? = null,
+ speaker: Speaker,
+ modifier: Modifier = Modifier,
+ navigateToSpeakerDetails: (String) -> Unit,
+ sharedTransitionScope: SharedTransitionScope? = null,
+ animatedVisibilityScope: AnimatedVisibilityScope? = null,
) {
val circularShape = if (LocalIsNeobrutalism.current) RectangleShape else CircleShape
@@ -245,13 +244,13 @@ fun SpeakerItem(
.fillMaxWidth()
.neoBrutalElevation()
.clickable(onClick = { navigateToSpeakerDetails(speaker.id) }),
- shape = MaterialTheme.shapes.large,
- color = MaterialTheme.colorScheme.surfaceContainerHigh,
+ shape = MaterialTheme.shapes.large,
+ color = MaterialTheme.colorScheme.surfaceContainerHigh,
) {
Row(
- modifier = Modifier.padding(16.dp),
- horizontalArrangement = Arrangement.spacedBy(16.dp),
- verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(16.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
speaker.photoUrl?.let { url ->
val photoModifier = if (sharedTransitionScope != null && animatedVisibilityScope != null) {
@@ -265,23 +264,23 @@ fun SpeakerItem(
Modifier
}
AsyncImage(
- model = url,
- modifier = photoModifier
- .size(56.dp)
- .clip(circularShape),
- contentDescription = stringResource(Res.string.speakers)
+ model = url,
+ modifier = photoModifier
+ .size(56.dp)
+ .clip(circularShape),
+ contentDescription = stringResource(Res.string.speakers)
)
}
Column {
Text(
- text = speaker.name.orEmpty(),
- style = MaterialTheme.typography.titleMedium,
+ text = speaker.name.orEmpty(),
+ style = MaterialTheme.typography.titleMedium,
)
speaker.company?.let { company ->
Text(
- text = company,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
+ text = company,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}