diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..130cf2e07 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*.{kt,kts}] +end_of_line = lf +ij_kotlin_packages_to_use_import_on_demand = true +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true +ij_kotlin_imports_layout = * +ij_kotlin_indent_before_arrow_on_new_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +indent_size = 4 +indent_style = space +insert_final_newline = true +parameter-list-wrapping = true +ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than = 8 +ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 4 +ktlint_code_style = android_studio +ktlint_enum_entry_name_casing = upper_or_camel_cases +ktlint_function_naming_ignore_when_annotated_with = Composable +ktlint_function_signature_body_expression_wrapping = default +ktlint_ignore_back_ticked_identifier = false +max_line_length = 140 + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..cb803b22d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: Build +on: + pull_request: + push: + branches: [ main ] # Trigger on pushes to the main branch + workflow_dispatch: + +jobs: + build: + runs-on: macos-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'corretto' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Check, Assemble Android and compile iOS + run: ./gradlew ktlintCheck assembleDebug compileKotlinIosX64 --no-daemon \ No newline at end of file diff --git a/.github/workflows/gradle-wrapper.yaml b/.github/workflows/gradle-wrapper.yaml new file mode 100644 index 000000000..8f6a8f108 --- /dev/null +++ b/.github/workflows/gradle-wrapper.yaml @@ -0,0 +1,15 @@ +name: gradle-wrapper + +on: + pull_request: + paths: + - 'gradlew' + - 'gradlew.bat' + - 'gradle/wrapper/**' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: gradle/actions/wrapper-validation@v4 diff --git a/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml deleted file mode 100644 index c34083172..000000000 --- a/.github/workflows/ktlint.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: ktlint - -on: - pull_request: - -jobs: - ktlint: - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: "adopt" - java-version: "17" - - name: run ktlint - run: ./gradlew ktlintCheck diff --git a/.gitignore b/.gitignore index 86fdc5392..1f139b6f6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,17 +38,7 @@ captures/ *.iml .gradle /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -/.idea/encodings.xml -.idea/saveactions_settings.xml -.idea/deploymentTargetDropDown.xml -.idea/gradle.xml -.idea/misc.xml + .DS_Store /build /captures @@ -87,8 +77,14 @@ GoogleService-Info.plist GoogleService-Info.plist Pods/ *.xcworkspace -.idea/inspectionProfiles/profiles_settings.xml -.idea/inspectionProfiles/Project_Default.xml -.idea/dictionaries/matyas.xml -/.idea/ .kotlin +.idea/.name +.idea/compiler.xml +.idea/deploymentTargetSelector.xml +.idea/gradle.xml +.idea/kotlinc.xml +.idea/misc.xml +.idea/runConfigurations.xml +.idea/vcs.xml +.idea/workspace.xml +.idea/caches/deviceStreaming.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..6c0b91968 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..0f7bc519d --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml new file mode 100644 index 000000000..7d04a74be --- /dev/null +++ b/.idea/inspectionProfiles/ktlint.xml @@ -0,0 +1,7 @@ + + + + diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..64580d143 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + diff --git a/README.md b/README.md index 19e7a2cb1..c33e74e9d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This project has a pair of native mobile applications backed by the Sessionize d > ## Subscribe! > > We build solutions that get teams started smoothly with Kotlin Multiplatform and ensure their success in production. Join our community to learn how your peers are adopting KMM. -[Sign up here](https://form.typeform.com/to/MJTpmm?typeform-source=touchlab.co)! +[Sign up here](https://touchlab.co/?s=shownewsletter)! ## Building diff --git a/android/build.gradle.kts b/android/build.gradle.kts index d3c7bce71..463377b60 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -5,6 +5,8 @@ plugins { kotlin("android") alias(libs.plugins.googleServices) alias(libs.plugins.crashlytics) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.composeCompiler) } val releaseEnabled = file("./release.jks").exists() @@ -24,8 +26,8 @@ android { applicationId = "co.touchlab.droidcon.london" minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() - versionCode = 20201 - versionName = "2.2.0" + versionCode = 60107 + versionName = "6.1.7" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } packaging { @@ -63,9 +65,6 @@ android { abortOnError = false } - buildFeatures { - compose = true - } composeOptions { kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() } @@ -88,6 +87,7 @@ dependencies { implementation(platform(libs.firebase.bom)) implementation(libs.firebase.analytics) implementation(libs.firebase.crashlytics) + implementation(libs.firebase.messaging) implementation(libs.hyperdrive.multiplatformx.api) @@ -95,3 +95,10 @@ dependencies { coreLibraryDesugaring(libs.android.desugar) } + +// Function that copies `mock` JSON config file for google-services if there isn't one available +// Google-services plugin requires this config file to build +val googleServices = file("google-services.json") +if (!googleServices.exists()) { + file("mock-google-services.json").copyTo(googleServices, overwrite = false) +} diff --git a/android/mock-google-services.json b/android/mock-google-services.json new file mode 100644 index 000000000..280d2302a --- /dev/null +++ b/android/mock-google-services.json @@ -0,0 +1,47 @@ +{ + "project_info": { + "project_number": "606665771234", + "project_id": "mock-firebase-project", + "storage_bucket": "mock-firebase-project.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:606665771229:android:c1f0f09aa42abc12", + "android_client_info": { + "package_name": "co.touchlab.droidcon.london" + } + }, + "oauth_client": [ + { + "client_id": "123455771229-sc9bpuefbjceq7i1qabk7gssstefrdlv.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "co.touchlab.droidcon.london", + "certificate_hash": "7f254b538565cfe6c28a88744985f015b1534980" + } + }, + { + "client_id": "123455771229-sc9bpuefbjceq7i1qabk7gssstefrdlv.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCNzhU2a9gMc_JHurHbywOrRI9Vj4VQZZZ" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "413392989754-04u9tv32474rj0pmbfksirt4ti02a64r.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index e01d5d2ac..8881129e3 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,9 +1,17 @@ - - - + - + + + + + + + + - + + + + + + + diff --git a/android/src/main/ic_launcher-playstore.png b/android/src/main/ic_launcher-playstore.png index c9773be4e..d3ed8bae1 100644 Binary files a/android/src/main/ic_launcher-playstore.png and b/android/src/main/ic_launcher-playstore.png differ diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt index 68fd1ad6f..a5c4e27b9 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt @@ -1,9 +1,13 @@ package co.touchlab.droidcon.android +import android.Manifest import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.Crossfade import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -17,12 +21,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import co.touchlab.droidcon.R import co.touchlab.droidcon.application.service.NotificationSchedulingService -import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.service.AnalyticsService import co.touchlab.droidcon.domain.service.SyncService import co.touchlab.droidcon.service.AndroidNotificationService @@ -31,23 +37,31 @@ import co.touchlab.droidcon.ui.util.MainView import co.touchlab.droidcon.util.AppChecker import co.touchlab.droidcon.util.NavigationController import co.touchlab.droidcon.viewmodel.ApplicationViewModel -import co.touchlab.kermit.Logger import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.brightify.hyperdrive.multiplatformx.LifecycleGraph import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class MainActivity : ComponentActivity(), KoinComponent { +class MainActivity : + ComponentActivity(), + KoinComponent { private val notificationSchedulingService: NotificationSchedulingService by inject() private val syncService: SyncService by inject() private val analyticsService: AnalyticsService by inject() + private val notificationService: AndroidNotificationService by inject() private val applicationViewModel: ApplicationViewModel by inject() private val root = LifecycleGraph.Root(this) + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { isGranted: Boolean -> + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -60,11 +74,15 @@ class MainActivity : ComponentActivity(), KoinComponent { applicationViewModel.lifecycle.removeFromParent() root.addChild(applicationViewModel.lifecycle) - lifecycleScope.launchWhenCreated { - syncService.runSynchronization() + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + syncService.runSynchronization() + } } - handleNotificationDeeplink(intent) + lifecycleScope.launch { + notificationService.handleNotificationDeeplink(intent) + } WindowCompat.setDecorFitsSystemWindows(window, false) @@ -72,6 +90,11 @@ class MainActivity : ComponentActivity(), KoinComponent { MainView(viewModel = applicationViewModel) val showSplashScreen by applicationViewModel.showSplashScreen.collectAsState() + if (!showSplashScreen) { + LaunchedEffect(Unit) { + askNotificationPermission() + } + } Crossfade(targetState = showSplashScreen) { shouldShowSplashScreen -> if (shouldShowSplashScreen) { LaunchedEffect(applicationViewModel) { @@ -82,12 +105,12 @@ class MainActivity : ComponentActivity(), KoinComponent { modifier = Modifier .background(Colors.primary) .fillMaxSize(), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Image( painter = painterResource(id = R.drawable.ic_splash_screen), contentDescription = getString(R.string.droidcon_title), - modifier = Modifier.padding(32.dp) + modifier = Modifier.padding(32.dp), ) } } @@ -104,26 +127,12 @@ class MainActivity : ComponentActivity(), KoinComponent { } } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - intent?.let(::handleNotificationDeeplink) - } - - private fun handleNotificationDeeplink(intent: Intent) { - val type = intent.getStringExtra(AndroidNotificationService.NOTIFICATION_TYPE_EXTRA_KEY) ?: return - val sessionId = intent.getStringExtra(AndroidNotificationService.NOTIFICATION_SESSION_ID_EXTRA_KEY) ?: return - applicationViewModel.notificationReceived( - sessionId, - when (type) { - AndroidNotificationService.NOTIFICATION_TYPE_EXTRA_REMINDER -> NotificationService.NotificationType.Reminder - AndroidNotificationService.NOTIFICATION_TYPE_EXTRA_FEEDBACK -> NotificationService.NotificationType.Feedback - else -> { - Logger.w("Unknown notification type $type.") - return - } - } - ) + lifecycleScope.launch { + notificationService.handleNotificationDeeplink(intent) + } } override fun onResume() { @@ -144,4 +153,18 @@ class MainActivity : ComponentActivity(), KoinComponent { super.onBackPressed() } } + + private fun askNotificationPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + } else if (false && shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + // TODO: display an educational UI explaining to the user the features that will be enabled + // by them granting the POST_NOTIFICATION permission. This UI should provide the user + // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. + // If the user selects "No thanks," allow the user to continue without notifications. + } else { + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } + } + } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt index b96e87dcd..538bfef59 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt @@ -31,7 +31,7 @@ class MainApp : Application() { single { this@MainApp } single> { MainActivity::class.java } single { - get().getSharedPreferences("DROIDCON_SETTINGS_2023", Context.MODE_PRIVATE) + get().getSharedPreferences("DROIDCON_SETTINGS_2024", Context.MODE_PRIVATE) } single { SharedPreferencesSettings(delegate = get()) } @@ -50,7 +50,7 @@ class MainApp : Application() { single { AndroidAnalyticsService(firebaseAnalytics = Firebase.analytics) } - } + uiModule + } + uiModule, ) } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt new file mode 100644 index 000000000..8f19da9f5 --- /dev/null +++ b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt @@ -0,0 +1,47 @@ +package co.touchlab.droidcon.android.service.impl + +import android.app.NotificationManager +import androidx.core.app.NotificationCompat +import androidx.core.content.getSystemService +import co.touchlab.droidcon.application.service.Notification +import co.touchlab.droidcon.service.AndroidNotificationService +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject + +class DefaultFirebaseMessagingService : FirebaseMessagingService() { + private val notificationService: AndroidNotificationService by inject() + + override fun onNewToken(token: String) { + super.onNewToken(token) + } + + override fun onMessageReceived(message: RemoteMessage) { + super.onMessageReceived(message) + + if (message.data.isNotEmpty() && message.data[Notification.Keys.NOTIFICATION_TYPE] == Notification.Values.REFRESH_DATA_TYPE) { + MainScope().launch { + notificationService.handleNotification( + Notification.Remote.RefreshData, + ) + } + } + + // If we have notification, we're running in foreground and should show it ourselves. + val originalNotification = message.notification ?: return + val notification = NotificationCompat.Builder(this, message.notification?.channelId ?: "") + .setContentTitle(originalNotification.title) + .setContentText(originalNotification.body) + .apply { + originalNotification.channelId?.let { + setChannelId(it) + } + } + .build() + + val notificationManager = getSystemService() + notificationManager?.notify(0, notification) + } +} diff --git a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultParseUrlViewService.kt b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultParseUrlViewService.kt index 14d7e86d2..0824b44f6 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultParseUrlViewService.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultParseUrlViewService.kt @@ -8,7 +8,5 @@ class DefaultParseUrlViewService : ParseUrlViewService { private val urlRegex = Patterns.WEB_URL.toRegex() - override fun parse(text: String): List { - return urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() - } + override fun parse(text: String): List = urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() } diff --git a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt index 96f07e388..41856c259 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt @@ -4,24 +4,16 @@ import android.content.Context import co.touchlab.droidcon.R import co.touchlab.droidcon.application.service.NotificationSchedulingService -class NotificationLocalizedStringFactory( - private val context: Context, -) : NotificationSchedulingService.LocalizedStringFactory { +class NotificationLocalizedStringFactory(private val context: Context) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { val ending = roomName?.let { context.getString(R.string.notification_reminder_title_in_room, it) } ?: "" return context.getString(R.string.notification_reminder_title_base, ending) } - override fun reminderBody(sessionTitle: String): String { - return context.getString(R.string.notification_reminder_body, sessionTitle) - } + override fun reminderBody(sessionTitle: String): String = context.getString(R.string.notification_reminder_body, sessionTitle) - override fun feedbackTitle(): String { - return context.getString(R.string.notification_feedback_title) - } + override fun feedbackTitle(): String = context.getString(R.string.notification_feedback_title) - override fun feedbackBody(): String { - return context.getString(R.string.notification_feedback_body) - } + override fun feedbackBody(): String = context.getString(R.string.notification_feedback_body) } diff --git a/android/src/main/res/drawable-night/about_droidcon.xml b/android/src/main/res/drawable-night/about_droidcon.xml new file mode 100644 index 000000000..7e50d5758 --- /dev/null +++ b/android/src/main/res/drawable-night/about_droidcon.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + diff --git a/android/src/main/res/drawable-night/about_kotlin.xml b/android/src/main/res/drawable-night/about_kotlin.xml new file mode 100644 index 000000000..0d03ca44a --- /dev/null +++ b/android/src/main/res/drawable-night/about_kotlin.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable-nodpi/about_droidcon.png b/android/src/main/res/drawable-nodpi/about_droidcon.png deleted file mode 100644 index 9b72a6ae1..000000000 Binary files a/android/src/main/res/drawable-nodpi/about_droidcon.png and /dev/null differ diff --git a/android/src/main/res/drawable-nodpi/about_kotlin.png b/android/src/main/res/drawable-nodpi/about_kotlin.png deleted file mode 100644 index 810b50066..000000000 Binary files a/android/src/main/res/drawable-nodpi/about_kotlin.png and /dev/null differ diff --git a/android/src/main/res/drawable/about_droidcon.xml b/android/src/main/res/drawable/about_droidcon.xml new file mode 100644 index 000000000..d9378725b --- /dev/null +++ b/android/src/main/res/drawable/about_droidcon.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/android/src/main/res/drawable/about_kotlin.xml b/android/src/main/res/drawable/about_kotlin.xml new file mode 100644 index 000000000..18bed16fc --- /dev/null +++ b/android/src/main/res/drawable/about_kotlin.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml b/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml index 98cf2e2a9..9087f64a5 100755 --- a/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml +++ b/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,12h-5v5h5v-5zM16,1v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2h-1L18,1h-2zM19,19L5,19L5,8h14v11z" /> diff --git a/android/src/main/res/drawable/ic_launcher_foreground.xml b/android/src/main/res/drawable/ic_launcher_foreground.xml index 68777bebb..a86fd7b05 100644 --- a/android/src/main/res/drawable/ic_launcher_foreground.xml +++ b/android/src/main/res/drawable/ic_launcher_foreground.xml @@ -3,270 +3,424 @@ android:height="108dp" android:viewportWidth="1024" android:viewportHeight="1024"> - + + + + + + + + + android:pathData="M2.8,-0.4h1023.9v1024.7h-1023.9z"/> - + android:pathData="M747.6,262.9h73.6v39.5h-73.6z" + android:strokeLineJoin="round" + android:strokeWidth="5.2" + android:fillColor="#0014e5" + android:strokeColor="#fff"/> + + + + + + + + + + android:fillColor="#0014e5"/> + android:fillColor="#fff"/> + android:fillColor="#0014e5"/> + android:fillColor="#fff"/> + android:fillColor="#0014e5"/> + android:fillColor="#fff"/> + android:fillColor="#0014e5"/> + + android:fillColor="#0014e5"/> + + + + + + + + android:fillColor="#0014e5"/> + android:pathData="M766.3,604.6v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M797.3,604.6L797.3,572.3" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M791.5,604.6v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M823.3,604.6L823.3,572.3" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M817.5,604.6v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M746.3,683.9L746.3,651.6" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M740.4,683.9v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M772.1,683.9L772.1,651.6" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M766.3,683.9v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M797.3,683.9L797.3,651.6" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M791.5,683.9v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M823.3,683.9L823.3,651.6" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M817.5,683.9v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M746.3,763.2L746.3,730.9" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M740.4,763.2v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M772.1,763.2L772.1,730.9" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M766.3,763.2v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M797.3,763.2L797.3,730.9" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M791.5,763.2v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M823.3,763.2L823.3,730.9" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M817.5,763.2v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M746.3,842.5L746.3,810.2" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M740.4,842.5v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M772.1,842.5L772.1,810.2" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M766.3,842.5v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M797.3,842.5L797.3,810.2" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M791.5,842.5v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M823.3,842.5L823.3,810.2" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M817.5,842.5v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M746.3,921.8L746.3,889.5" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M740.4,921.8v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M772.1,921.8L772.1,889.5" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M766.3,921.8v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M797.3,921.8L797.3,889.5" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M791.5,921.8v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M823.3,921.8L823.3,889.5" + android:strokeWidth="0" + android:fillColor="#0014e5"/> + android:pathData="M817.5,921.8v-32.3c0,-3.2 2.6,-5.8 5.8,-5.8s5.8,2.6 5.8,5.8v32.3h-11.7Z" + android:strokeWidth="0" + android:fillColor="#fff"/> + android:pathData="M721.5,606.6L848,606.6" + android:strokeLineJoin="round" + android:strokeWidth="5.2" + android:fillColor="#0014e5" + android:strokeColor="#fff" + android:strokeLineCap="round"/> + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/ic_splash_screen.xml b/android/src/main/res/drawable/ic_splash_screen.xml index d00ea40af..9796a73f3 100644 --- a/android/src/main/res/drawable/ic_splash_screen.xml +++ b/android/src/main/res/drawable/ic_splash_screen.xml @@ -8,386 +8,343 @@ android:strokeWidth="0" android:fillColor="#7de1c3"/> + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/linkedin.xml b/android/src/main/res/drawable/linkedin.xml index b58dac324..b9ed41646 100644 --- a/android/src/main/res/drawable/linkedin.xml +++ b/android/src/main/res/drawable/linkedin.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="72" + android:viewportHeight="72"> +V31.4h6.8v3.1h0.1c0.9-1.8,3.3-3.7,6.7-3.7c7.2,0,8.5,4.7,8.5,10.9V54.4z" /> diff --git a/android/src/main/res/drawable/twitter.xml b/android/src/main/res/drawable/twitter.xml index edd5d3001..3241156c7 100644 --- a/android/src/main/res/drawable/twitter.xml +++ b/android/src/main/res/drawable/twitter.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportHeight="72" + android:viewportWidth="72"> +c0-0.4,0-0.9,0-1.3C57,24.8,58.7,23.1,60,21.1Z" /> diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 7353dbd1f..eb21e25b9 100644 --- a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,7 @@ - - \ No newline at end of file + + + + diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml index d65b2ad6d..9d0fc74c0 100644 --- a/android/src/main/res/values/colors.xml +++ b/android/src/main/res/values/colors.xml @@ -1,4 +1,5 @@ #FFffffff + #FF7de1c3 diff --git a/android/src/main/res/values/ic_launcher_background.xml b/android/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index d4bad665f..000000000 --- a/android/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #64E2C3 - \ No newline at end of file diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index c7f9d72c1..4cb1278a2 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -7,6 +7,6 @@ Feedback Time! Your Feedback is Requested. - Droidcon London 2023 + Droidcon London 2024 diff --git a/android/src/main/res/values/themes.xml b/android/src/main/res/values/themes.xml index 27cd65174..6ab3e289b 100644 --- a/android/src/main/res/values/themes.xml +++ b/android/src/main/res/values/themes.xml @@ -12,8 +12,7 @@ @style/Theme.Droidcon - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/LaunchScreen/LaunchScreen_Icon.imageset/london23-splash-screen.png b/ios/Droidcon/Droidcon/Assets.xcassets/LaunchScreen/LaunchScreen_Icon.imageset/london23-splash-screen.png deleted file mode 100644 index b520d6aaf..000000000 Binary files a/ios/Droidcon/Droidcon/Assets.xcassets/LaunchScreen/LaunchScreen_Icon.imageset/london23-splash-screen.png and /dev/null differ diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/Contents.json b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/Contents.json index b2d646dd9..a6f1adb2a 100644 --- a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/Contents.json +++ b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/Contents.json @@ -1,21 +1,25 @@ { "images" : [ { - "filename" : "droidconbar.png", - "idiom" : "universal", - "scale" : "1x" + "filename" : "droidcon_logo_light.pdf", + "idiom" : "universal" }, { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "droidcon_logo_night.pdf", + "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_light.pdf b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_light.pdf new file mode 100644 index 000000000..d9015231d Binary files /dev/null and b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_light.pdf differ diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_night.pdf b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_night.pdf new file mode 100644 index 000000000..5641206b1 Binary files /dev/null and b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidcon_logo_night.pdf differ diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidconbar.png b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidconbar.png deleted file mode 100644 index eee5c726e..000000000 Binary files a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_droidcon.imageset/droidconbar.png and /dev/null differ diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/Contents.json b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/Contents.json index b58a2cfdc..09540a34b 100644 --- a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/Contents.json +++ b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/Contents.json @@ -1,21 +1,25 @@ { "images" : [ { - "idiom" : "universal", - "scale" : "1x" + "filename" : "small Kotlin Full Color Logo on White RGB.svg", + "idiom" : "universal" }, { - "filename" : "kotlinbar.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "small Kotlin Full Color Logo on Black RGB.svg", + "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/kotlinbar.png b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/kotlinbar.png deleted file mode 100644 index 810b50066..000000000 Binary files a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/kotlinbar.png and /dev/null differ diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on Black RGB.svg b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on Black RGB.svg new file mode 100644 index 000000000..8fd3eb068 --- /dev/null +++ b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on Black RGB.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on White RGB.svg b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on White RGB.svg new file mode 100644 index 000000000..218d62813 --- /dev/null +++ b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_kotlin.imageset/small Kotlin Full Color Logo on White RGB.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_touchlab.imageset/Contents.json b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_touchlab.imageset/Contents.json index 6758dbb36..8c269c78c 100644 --- a/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_touchlab.imageset/Contents.json +++ b/ios/Droidcon/Droidcon/Assets.xcassets/Logos/about_touchlab.imageset/Contents.json @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/Droidcon/Droidcon/Base.lproj/LaunchScreen.storyboard b/ios/Droidcon/Droidcon/Base.lproj/LaunchScreen.storyboard index b0a832297..d3c984092 100644 --- a/ios/Droidcon/Droidcon/Base.lproj/LaunchScreen.storyboard +++ b/ios/Droidcon/Droidcon/Base.lproj/LaunchScreen.storyboard @@ -1,9 +1,9 @@ - + - + @@ -17,10 +17,10 @@ - - + + - + @@ -28,7 +28,7 @@ - + @@ -42,9 +42,9 @@ - + - + diff --git a/ios/Droidcon/Droidcon/Common/Avatar.swift b/ios/Droidcon/Droidcon/Common/Avatar.swift index 8c53984f0..cb4b05aa2 100644 --- a/ios/Droidcon/Droidcon/Common/Avatar.swift +++ b/ios/Droidcon/Droidcon/Common/Avatar.swift @@ -1,5 +1,4 @@ import SwiftUI -import Kingfisher struct Avatar: View { private(set) var url: URL @@ -7,8 +6,11 @@ struct Avatar: View { private(set) var size: CGFloat var body: some View { - KFImage(url) - .resizable() + AsyncImage(url: url) { image in + image.resizable() + } placeholder: { + Color.accentColor + } .scaledToFill() .frame(width: size, height: size) .aspectRatio(1, contentMode: .fill) diff --git a/ios/Droidcon/Droidcon/Common/FeedbackDialog.swift b/ios/Droidcon/Droidcon/Common/FeedbackDialog.swift index 0e2e32bfd..546d4fb43 100644 --- a/ios/Droidcon/Droidcon/Common/FeedbackDialog.swift +++ b/ios/Droidcon/Droidcon/Common/FeedbackDialog.swift @@ -91,8 +91,6 @@ struct FeedbackDialog: View { imageName = "Feedback_Normal" case .satisfied: imageName = "Feedback_Satisfied" - default: - fatalError("Unknown image for rating '\(rating)'.") } let isSelected = selectedRating.wrappedValue == rating diff --git a/ios/Droidcon/Droidcon/ComposeController.swift b/ios/Droidcon/Droidcon/ComposeController.swift index 43b73df95..2715ee62f 100644 --- a/ios/Droidcon/Droidcon/ComposeController.swift +++ b/ios/Droidcon/Droidcon/ComposeController.swift @@ -6,7 +6,7 @@ struct ComposeController: UIViewControllerRepresentable { let viewModel: ApplicationViewModel func makeUIViewController(context: Context) -> some UIViewController { - ComposeRootControllerKt.getRootController(viewModel: viewModel) + getRootController(viewModel: viewModel) } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} diff --git a/ios/Droidcon/Droidcon/Droidcon.entitlements b/ios/Droidcon/Droidcon/Droidcon.entitlements new file mode 100644 index 000000000..903def2af --- /dev/null +++ b/ios/Droidcon/Droidcon/Droidcon.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ios/Droidcon/Droidcon/DroidconApp.swift b/ios/Droidcon/Droidcon/DroidconApp.swift index 8722d7a20..6a1980374 100644 --- a/ios/Droidcon/Droidcon/DroidconApp.swift +++ b/ios/Droidcon/Droidcon/DroidconApp.swift @@ -25,6 +25,7 @@ struct DroidconApp: App { private func setupNavBarAppearance() { let appearance = UINavigationBarAppearance() + appearance.configureWithDefaultBackground() appearance.backgroundColor = UIColor(named: "NavBar_Background") appearance.titleTextAttributes = UIColor(named: "NavBar_Foreground").map { [.foregroundColor: $0] } ?? [:] UINavigationBar.appearance().tintColor = UIColor(named: "NavBar_Foreground") @@ -41,7 +42,7 @@ struct DroidconApp: App { itemAppearance.selected.iconColor = UIColor(named: "TabBar_Foreground_Selected") itemAppearance.selected.titleTextAttributes = UIColor(named: "TabBar_Foreground_Selected").map { [.foregroundColor: $0] } ?? [:] let appearance = UITabBarAppearance() - appearance.configureWithOpaqueBackground() + appearance.configureWithDefaultBackground() appearance.backgroundColor = UIColor(named: "TabBar_Background") appearance.inlineLayoutAppearance = itemAppearance appearance.compactInlineLayoutAppearance = itemAppearance diff --git a/ios/Droidcon/Droidcon/Info.plist b/ios/Droidcon/Droidcon/Info.plist index faf69c154..071acbe31 100644 --- a/ios/Droidcon/Droidcon/Info.plist +++ b/ios/Droidcon/Droidcon/Info.plist @@ -2,6 +2,10 @@ + LSApplicationQueriesSchemes + + fluttercon + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -13,15 +17,35 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Droidcon London + Droidcon London 2024 CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + droidconlondon + CFBundleURLSchemes + + droidconlondon + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) + FirebaseAppDelegateProxyEnabled + LSRequiresIPhoneOS + UIBackgroundModes + + fetch + remote-notification + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/ios/Droidcon/Droidcon/Koin.swift b/ios/Droidcon/Droidcon/Koin.swift index 66359b9a8..a35e2ee21 100644 --- a/ios/Droidcon/Droidcon/Koin.swift +++ b/ios/Droidcon/Droidcon/Koin.swift @@ -2,7 +2,7 @@ import Foundation import DroidconKit func startKoin() { - let userDefaults = UserDefaults(suiteName: "DROIDCON2023_SETTINGS")! + let userDefaults = UserDefaults(suiteName: "DROIDCON2024_SETTINGS")! let koinApplication = DependencyInjectionKt.doInitKoinIos(userDefaults: userDefaults, analyticsService: IOSAnalyticsService()) _koin = koinApplication.koin @@ -12,3 +12,15 @@ private var _koin: Koin_coreKoin? = nil var koin: Koin_coreKoin { return _koin! } + +extension Koin_coreKoin { + func get(_ type: T.Type = T.self, qualifier: Koin_coreQualifier? = nil, parameters: Any...) -> T { + return getAny( + objCObject: type, + qualifier: qualifier, + parameters: parameters.isEmpty ? nil : { + Koin_coreParametersHolder(_values: NSMutableArray(array: parameters), useIndexedValues: KotlinBoolean(bool: false)) + } + ) as! T + } +} diff --git a/ios/Droidcon/Droidcon/MainView.swift b/ios/Droidcon/Droidcon/MainView.swift index e0cfdcea6..f6937fcd8 100644 --- a/ios/Droidcon/Droidcon/MainView.swift +++ b/ios/Droidcon/Droidcon/MainView.swift @@ -10,7 +10,7 @@ struct MainView: View { TabView(selection: $viewModel.selectedTab) { ForEach(viewModel.tabs, id: \.self) { tab in switch (tab) { - case ApplicationViewModel.Tab.schedule: + case .schedule: ScheduleView( viewModel: viewModel.schedule, navigationTitle: "Schedule.Title" @@ -20,7 +20,7 @@ struct MainView: View { Text("Schedule.TabItem.Title") } .tag(tab); - case ApplicationViewModel.Tab.myagenda: + case .myAgenda: ScheduleView( viewModel: viewModel.agenda, navigationTitle: "Agenda.Title" @@ -30,7 +30,7 @@ struct MainView: View { Text("Agenda.TabItem.Title") } .tag(tab); - case ApplicationViewModel.Tab.sponsors: + case .sponsors: SponsorListView( viewModel: viewModel.sponsors, navigationTitle: "Sponsors.Title" @@ -40,7 +40,7 @@ struct MainView: View { Text("Sponsors.TabItem.Title") } .tag(tab); - case ApplicationViewModel.Tab.settings: + case .settings: SettingsView( viewModel: viewModel.settings ) @@ -49,8 +49,12 @@ struct MainView: View { Text("Settings.TabItem.Title") } .tag(tab); - default: - fatalError("Unknown tab \(tab).") + case .venue: + VenueView() + .tabItem { + Image(systemName: "map") + Text("Venue.TabItem.Title") + } } } } diff --git a/ios/Droidcon/Droidcon/Sessions/Detail/SessionDetailView.swift b/ios/Droidcon/Droidcon/Sessions/Detail/SessionDetailView.swift index ebb35211f..ca320c8cb 100644 --- a/ios/Droidcon/Droidcon/Sessions/Detail/SessionDetailView.swift +++ b/ios/Droidcon/Droidcon/Sessions/Detail/SessionDetailView.swift @@ -129,14 +129,12 @@ struct SessionDetailView: View { private func stateMessage(from state: SessionDetailViewModel.SessionState) -> LocalizedStringKey? { switch state { - case .inconflict: + case .inConflict: return "Session.Detail.State.Conflict" - case .inprogress: + case .inProgress: return "Session.Detail.State.InProgress" case .ended: return "Session.Detail.State.Ended" - default: - return nil } } } diff --git a/ios/Droidcon/Droidcon/Sessions/Detail/SpeakerListItemView.swift b/ios/Droidcon/Droidcon/Sessions/Detail/SpeakerListItemView.swift index c4ea1c01a..cd103558b 100644 --- a/ios/Droidcon/Droidcon/Sessions/Detail/SpeakerListItemView.swift +++ b/ios/Droidcon/Droidcon/Sessions/Detail/SpeakerListItemView.swift @@ -1,5 +1,4 @@ import SwiftUI -import Kingfisher import DroidconKit struct SpeakerListItemView: View { diff --git a/ios/Droidcon/Droidcon/Settings/SettingsView.swift b/ios/Droidcon/Droidcon/Settings/SettingsView.swift index c3eb06f82..e1dacc3b2 100644 --- a/ios/Droidcon/Droidcon/Settings/SettingsView.swift +++ b/ios/Droidcon/Droidcon/Settings/SettingsView.swift @@ -40,6 +40,14 @@ struct SettingsView: View { } .navigationTitle("Settings.Title") .navigationBarTitleDisplayMode(.inline) + .toolbar { + if(Constants.SisterApp.shared.showLaunchButton){ + Button("Open \(Constants.SisterApp.shared.name)") { + openSisterApp() + } + .buttonStyle(BorderedProminentButtonStyle()) + } + } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/ios/Droidcon/Droidcon/Sponsors/SponsorDetailView.swift b/ios/Droidcon/Droidcon/Sponsors/SponsorDetailView.swift index f812e3db6..bdbed35b1 100644 --- a/ios/Droidcon/Droidcon/Sponsors/SponsorDetailView.swift +++ b/ios/Droidcon/Droidcon/Sponsors/SponsorDetailView.swift @@ -1,5 +1,4 @@ import SwiftUI -import Kingfisher import DroidconKit struct SponsorDetailView: View { @@ -29,8 +28,9 @@ struct SponsorDetailView: View { .frame(maxWidth: .infinity, alignment: .leading) if let imageUrl = URL(string: viewModel.imageUrl.string) { - KFImage(imageUrl) - .placeholder { + AsyncImage(url: imageUrl) { image in + image.resizable() + } placeholder: { GeometryReader { geometry in Text(viewModel.name) .bold() @@ -41,7 +41,6 @@ struct SponsorDetailView: View { .frame(height: geometry.size.width) } } - .resizable() .scaledToFit() .padding(4) .background(Color.white) diff --git a/ios/Droidcon/Droidcon/Sponsors/SponsorGroupItemView.swift b/ios/Droidcon/Droidcon/Sponsors/SponsorGroupItemView.swift index fe7cef5e7..9b5e5b9a1 100644 --- a/ios/Droidcon/Droidcon/Sponsors/SponsorGroupItemView.swift +++ b/ios/Droidcon/Droidcon/Sponsors/SponsorGroupItemView.swift @@ -1,5 +1,4 @@ import SwiftUI -import Kingfisher import DroidconKit struct SponsorGroupItemView: View { @@ -15,11 +14,11 @@ struct SponsorGroupItemView: View { .shadow(color: Color("Shadow"), radius: 2, y: 1) if let imageUrl = URL(string: viewModel.imageUrl.string) { - KFImage(imageUrl) - .placeholder { + AsyncImage(url: imageUrl) { image in + image.resizable() + } placeholder: { placeholder } - .resizable() .scaledToFit() .cornerRadius(.greatestFiniteMagnitude) .padding(4) diff --git a/ios/Droidcon/Droidcon/Venue/VenueView.swift b/ios/Droidcon/Droidcon/Venue/VenueView.swift new file mode 100644 index 000000000..7f77d557a --- /dev/null +++ b/ios/Droidcon/Droidcon/Venue/VenueView.swift @@ -0,0 +1,16 @@ +import SwiftUI +import DroidconKit + +struct VenueView: View { + var body: some View { + VenueBodyView() + } +} + +private struct VenueBodyView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> some UIViewController { + venueBodyViewController() + } + + func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} +} diff --git a/ios/Droidcon/Droidcon/en.lproj/Localizable.strings b/ios/Droidcon/Droidcon/en.lproj/Localizable.strings index 6790e7969..9bccab297 100644 --- a/ios/Droidcon/Droidcon/en.lproj/Localizable.strings +++ b/ios/Droidcon/Droidcon/en.lproj/Localizable.strings @@ -6,7 +6,7 @@ "Notification.Feedback.Title" = "Feedback Time!"; "Notification.Feedback.Body" = "Your Feedback is Requested."; -"Schedule.Title" = "Schedule"; +"Schedule.Title" = "Droidcon London 2024"; "Schedule.TabItem.Title" = "Schedule"; "Agenda.Title" = "Agenda"; @@ -45,3 +45,6 @@ "Feedback.Dialog.Submit" = "Submit"; "Feedback.Dialog.CloseAndDisable" = "Close and disable feedback"; "Feedback.Dialog.Skip" = "Skip feedback"; + +"Venue.Title" = "Venue Map"; +"Venue.TabItem.Title" = "Venue"; diff --git a/ios/Droidcon/Podfile b/ios/Droidcon/Podfile deleted file mode 100644 index 08d085866..000000000 --- a/ios/Droidcon/Podfile +++ /dev/null @@ -1,11 +0,0 @@ -use_frameworks! - -platform :ios, '14.0' - -install! 'cocoapods', :deterministic_uuids => false - -target 'Droidcon' do - pod 'Kingfisher', '~> 7.8.1' - pod 'Firebase/Analytics' - pod 'Firebase/Crashlytics' -end diff --git a/ios/Droidcon/Podfile.lock b/ios/Droidcon/Podfile.lock deleted file mode 100644 index 7f8b0e275..000000000 --- a/ios/Droidcon/Podfile.lock +++ /dev/null @@ -1,154 +0,0 @@ -PODS: - - Firebase/Analytics (10.15.0): - - Firebase/Core - - Firebase/Core (10.15.0): - - Firebase/CoreOnly - - FirebaseAnalytics (~> 10.15.0) - - Firebase/CoreOnly (10.15.0): - - FirebaseCore (= 10.15.0) - - Firebase/Crashlytics (10.15.0): - - Firebase/CoreOnly - - FirebaseCrashlytics (~> 10.15.0) - - FirebaseAnalytics (10.15.0): - - FirebaseAnalytics/AdIdSupport (= 10.15.0) - - FirebaseCore (~> 10.0) - - FirebaseInstallations (~> 10.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - - GoogleUtilities/MethodSwizzler (~> 7.11) - - GoogleUtilities/Network (~> 7.11) - - "GoogleUtilities/NSData+zlib (~> 7.11)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseAnalytics/AdIdSupport (10.15.0): - - FirebaseCore (~> 10.0) - - FirebaseInstallations (~> 10.0) - - GoogleAppMeasurement (= 10.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - - GoogleUtilities/MethodSwizzler (~> 7.11) - - GoogleUtilities/Network (~> 7.11) - - "GoogleUtilities/NSData+zlib (~> 7.11)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCore (10.15.0): - - FirebaseCoreInternal (~> 10.0) - - GoogleUtilities/Environment (~> 7.8) - - GoogleUtilities/Logger (~> 7.8) - - FirebaseCoreExtension (10.19.0): - - FirebaseCore (~> 10.0) - - FirebaseCoreInternal (10.19.0): - - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseCrashlytics (10.15.0): - - FirebaseCore (~> 10.5) - - FirebaseInstallations (~> 10.0) - - FirebaseSessions (~> 10.5) - - GoogleDataTransport (~> 9.2) - - GoogleUtilities/Environment (~> 7.8) - - nanopb (< 2.30910.0, >= 2.30908.0) - - PromisesObjC (~> 2.1) - - FirebaseInstallations (10.15.0): - - FirebaseCore (~> 10.0) - - GoogleUtilities/Environment (~> 7.8) - - GoogleUtilities/UserDefaults (~> 7.8) - - PromisesObjC (~> 2.1) - - FirebaseSessions (10.19.0): - - FirebaseCore (~> 10.5) - - FirebaseCoreExtension (~> 10.0) - - FirebaseInstallations (~> 10.0) - - GoogleDataTransport (~> 9.2) - - GoogleUtilities/Environment (~> 7.10) - - nanopb (< 2.30910.0, >= 2.30908.0) - - PromisesSwift (~> 2.1) - - GoogleAppMeasurement (10.15.0): - - GoogleAppMeasurement/AdIdSupport (= 10.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - - GoogleUtilities/MethodSwizzler (~> 7.11) - - GoogleUtilities/Network (~> 7.11) - - "GoogleUtilities/NSData+zlib (~> 7.11)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (10.15.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 10.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - - GoogleUtilities/MethodSwizzler (~> 7.11) - - GoogleUtilities/Network (~> 7.11) - - "GoogleUtilities/NSData+zlib (~> 7.11)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (10.15.0): - - GoogleUtilities/AppDelegateSwizzler (~> 7.11) - - GoogleUtilities/MethodSwizzler (~> 7.11) - - GoogleUtilities/Network (~> 7.11) - - "GoogleUtilities/NSData+zlib (~> 7.11)" - - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleDataTransport (9.2.5): - - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.11.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (7.11.5): - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.11.5): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.11.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (7.11.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.11.5)" - - GoogleUtilities/Reachability (7.11.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.11.5): - - GoogleUtilities/Logger - - Kingfisher (7.8.1) - - nanopb (2.30909.0): - - nanopb/decode (= 2.30909.0) - - nanopb/encode (= 2.30909.0) - - nanopb/decode (2.30909.0) - - nanopb/encode (2.30909.0) - - PromisesObjC (2.3.1) - - PromisesSwift (2.3.1): - - PromisesObjC (= 2.3.1) - -DEPENDENCIES: - - Firebase/Analytics - - Firebase/Crashlytics - - Kingfisher (~> 7.8.1) - -SPEC REPOS: - trunk: - - Firebase - - FirebaseAnalytics - - FirebaseCore - - FirebaseCoreExtension - - FirebaseCoreInternal - - FirebaseCrashlytics - - FirebaseInstallations - - FirebaseSessions - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleUtilities - - Kingfisher - - nanopb - - PromisesObjC - - PromisesSwift - -SPEC CHECKSUMS: - Firebase: 66043bd4579e5b73811f96829c694c7af8d67435 - FirebaseAnalytics: 47cef43728f81a839cf1306576bdd77ffa2eac7e - FirebaseCore: 2cec518b43635f96afe7ac3a9c513e47558abd2e - FirebaseCoreExtension: c08d14c7b22e07994e876d837e6f58642f340087 - FirebaseCoreInternal: b444828ea7cfd594fca83046b95db98a2be4f290 - FirebaseCrashlytics: a83f26fb922a3fe181eb738fb4dcf0c92bba6455 - FirebaseInstallations: cae95cab0f965ce05b805189de1d4c70b11c76fb - FirebaseSessions: e5f4caa188dc8bc6142abc974355be75b042215e - GoogleAppMeasurement: 722db6550d1e6d552b08398b69a975ac61039338 - GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 - GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 - Kingfisher: 63f677311d36a3473f6b978584f8a3845d023dc5 - nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 - PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 - PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 - -PODFILE CHECKSUM: 4654f3ccb4ae86c4f9440935f0df48a0ebdaaeb8 - -COCOAPODS: 1.14.3 diff --git a/ios/build.gradle.kts b/ios/build.gradle.kts index 797b5a089..abf762437 100644 --- a/ios/build.gradle.kts +++ b/ios/build.gradle.kts @@ -4,7 +4,9 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.serialization) - alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.skie) } version = "1.0" @@ -13,11 +15,9 @@ kotlin { val targets = listOf( iosX64(), iosArm64(), - iosSimulatorArm64() + iosSimulatorArm64(), ) - applyDefaultHierarchyTemplate() - sourceSets { all { languageSettings.apply { @@ -55,7 +55,7 @@ kotlin { "-linker-option", "-framework", "-linker-option", "Metal", "-linker-option", "-framework", "-linker-option", "CoreText", "-linker-option", "-framework", "-linker-option", "CoreGraphics", - "-Xdisable-phases=VerifyBitcode" + "-Xdisable-phases=VerifyBitcode", ) linkerOpts.add("-lsqlite3") export(libs.kermit) @@ -78,7 +78,7 @@ kotlin { } afterEvaluate { - tasks.withType() { + tasks.withType { (compilerPluginClasspath as? Configuration)?.isTransitive = true } } diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/DependencyInjection.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/DependencyInjection.kt index b72072461..cddc46316 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/DependencyInjection.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/DependencyInjection.kt @@ -22,10 +22,7 @@ import platform.Foundation.NSBundle import platform.Foundation.NSUserDefaults @OptIn(ExperimentalSettingsApi::class) -fun initKoinIos( - userDefaults: NSUserDefaults, - analyticsService: AnalyticsService -): KoinApplication = initKoin( +fun initKoinIos(userDefaults: NSUserDefaults, analyticsService: AnalyticsService): KoinApplication = initKoin( module { single { BundleProvider(bundle = NSBundle.mainBundle) } single { NSUserDefaultsSettings(delegate = userDefaults) } @@ -40,7 +37,7 @@ fun initKoinIos( single { analyticsService } single { DefaultParseUrlViewService() } - } + uiModule + } + uiModule, ) // Workaround class for injecting an `NSObject` class. diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/service/DefaultParseUrlViewService.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/service/DefaultParseUrlViewService.kt index 90dbc71e9..f4b4c1abe 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/service/DefaultParseUrlViewService.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/service/DefaultParseUrlViewService.kt @@ -8,7 +8,5 @@ class DefaultParseUrlViewService : ParseUrlViewService { private val urlRegex = "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex() - override fun parse(text: String): List { - return urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() - } + override fun parse(text: String): List = urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() } diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt index 783791d1a..21ee3bf5e 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt @@ -7,27 +7,35 @@ import platform.Foundation.NSString import platform.Foundation.stringWithFormat @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) -class NotificationLocalizedStringFactory( - private val bundle: NSBundle, -) : NotificationSchedulingService.LocalizedStringFactory { +class NotificationLocalizedStringFactory(private val bundle: NSBundle) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null).convertParametersForPrintf(), it.cstr) } ?: "" - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null).convertParametersForPrintf(), ending.cstr) + val ending = roomName?.let { + NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null) + .convertParametersForPrintf(), + it.cstr, + ) + } ?: "" + return NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null) + .convertParametersForPrintf(), + ending.cstr, + ) } - override fun reminderBody(sessionTitle: String): String { - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Body", null, null).convertParametersForPrintf(), sessionTitle.cstr) - } + override fun reminderBody(sessionTitle: String): String = NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Body", null, null) + .convertParametersForPrintf(), + sessionTitle.cstr, + ) - override fun feedbackTitle(): String { - return bundle.localizedStringForKey("Notification.Feedback.Title", null, null) - } + override fun feedbackTitle(): String = bundle.localizedStringForKey("Notification.Feedback.Title", null, null) - override fun feedbackBody(): String { - return bundle.localizedStringForKey("Notification.Feedback.Body", null, null) - } + override fun feedbackBody(): String = bundle.localizedStringForKey("Notification.Feedback.Body", null, null) - private fun String.convertParametersForPrintf(): String = - replace("%@", "%s") + private fun String.convertParametersForPrintf(): String = replace("%@", "%s") } diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt index 0e1385c70..501bb8151 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt @@ -34,21 +34,19 @@ class IOSDateFormatter : DateFormatter { } } - override fun monthWithDay(date: LocalDate) = - date.date()?.let { monthWithDay.stringFromDate(it) } + override fun monthWithDay(date: LocalDate) = date.date()?.let { monthWithDay.stringFromDate(it) } - override fun timeOnly(dateTime: LocalDateTime) = - dateTime.date()?.let { timeOnly.stringFromDate(it) } + override fun timeOnly(dateTime: LocalDateTime) = dateTime.date()?.let { timeOnly.stringFromDate(it) } override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime) = interval( fromDateTime.date()?.let { timeOnlyNoPeriod.stringFromDate(it) }, - toDateTime.date()?.let { timeOnly.stringFromDate(it) } + toDateTime.date()?.let { timeOnly.stringFromDate(it) }, ) private fun LocalDate.date() = NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) - private fun LocalDateTime.date() = - NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + private fun LocalDateTime.date() = NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun interval(from: String?, to: String?) = listOfNotNull(from, to).joinToString(" – ") } diff --git a/settings.gradle.kts b/settings.gradle.kts index d7907c793..55116d7de 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,10 @@ dependencyResolutionManagement { } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0") +} + include(":shared", ":shared-ui", ":android", ":ios") rootProject.name = "Droidcon" diff --git a/shared-ui/build.gradle.kts b/shared-ui/build.gradle.kts index 3b229897c..9828407a9 100644 --- a/shared-ui/build.gradle.kts +++ b/shared-ui/build.gradle.kts @@ -5,7 +5,8 @@ plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.serialization) alias(libs.plugins.androidLibrary) - alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.composeCompiler) } android { @@ -31,7 +32,7 @@ android { listOf( "src/androidMain/resources", "src/commonMain/resources", - ) + ), ) main.manifest.srcFile("src/androidMain/AndroidManifest.xml") } @@ -86,7 +87,6 @@ kotlin { implementation(libs.stately.common) implementation(libs.koin.core) - implementation(compose.ui) implementation(compose.foundation) implementation(compose.material3) @@ -95,13 +95,13 @@ kotlin { // https://issuetracker.google.com/issues/294869453 // https://github.com/JetBrains/compose-multiplatform/issues/3927 api(compose.runtime) + implementation(compose.components.resources) + + implementation(libs.zoomimage.composeResources) implementation(libs.hyperdrive.multiplatformx.api) // implementation(libs.hyperdrive.multiplatformx.compose) } - iosMain.dependencies { - implementation(libs.imageLoader) - } all { languageSettings.apply { optIn("kotlin.RequiresOptIn") diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt index f90801aba..b9fdec126 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt @@ -1,9 +1,37 @@ package co.touchlab.droidcon.ui.settings +import android.content.Intent +import android.net.Uri +import androidx.compose.material3.Button +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import co.touchlab.droidcon.Constants import co.touchlab.droidcon.viewmodel.settings.SettingsViewModel @Composable internal actual fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) { // Add settings specific for Android here. } + +@Composable +internal actual fun PlatformSwitchApp() { + val context = LocalContext.current + Button( + onClick = { + val intent = context.packageManager.getLaunchIntentForPackage(Constants.SisterApp.androidPackageName) + if (intent != null) { + context.startActivity(intent) + } else { + context.startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse("market://details?id=${Constants.SisterApp.androidPackageName}"), + ), + ) + } + }, + ) { + Text("Open ${Constants.SisterApp.name}") + } +} diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/theme/Type.android.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/theme/Type.android.kt index fd803324b..43de2f781 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/theme/Type.android.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/theme/Type.android.kt @@ -16,5 +16,5 @@ actual val montserratFontFamily: FontFamily = FontFamily( montserratRegularFont, montserratMediumFont, montserratSemiBoldFont, - montserratBoldFont + montserratBoldFont, ) diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt index 1a0409859..82eb170e7 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt @@ -2,8 +2,8 @@ package co.touchlab.droidcon.ui.util import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.Dialog as AndroidXComposeDialog +import androidx.compose.ui.window.DialogProperties @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt index 7ab278101..6f7813f4d 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt @@ -1,4 +1,6 @@ -package co.touchlab.droidcon.ui.util // ktlint-disable filename +@file:Suppress("ktlint:standard:filename") + +package co.touchlab.droidcon.ui.util import android.annotation.SuppressLint import androidx.compose.foundation.background @@ -32,19 +34,19 @@ internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, modifier = modifier, painter = painterResource(id = imageRes), contentDescription = contentDescription, - contentScale = ContentScale.FillWidth + contentScale = ContentScale.FillWidth, ) } else { Row( modifier = modifier.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(Dimensions.Padding.half)), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Spacer(modifier = Modifier.weight(1f)) Icon( imageVector = Icons.Default.Warning, contentDescription = contentDescription, modifier = Modifier.padding(Dimensions.Padding.half), - tint = Color.White + tint = Color.White, ) Text("Image not supported", modifier = Modifier.padding(Dimensions.Padding.default), color = Color.White) Spacer(modifier = Modifier.weight(1f)) diff --git a/shared-ui/src/commonMain/composeResources/drawable/venue-map-1.jpg b/shared-ui/src/commonMain/composeResources/drawable/venue-map-1.jpg new file mode 100644 index 000000000..9ed204942 Binary files /dev/null and b/shared-ui/src/commonMain/composeResources/drawable/venue-map-1.jpg differ diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt index 53cf5696f..ee2375768 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CalendarMonth import androidx.compose.material.icons.filled.LocalFireDepartment +import androidx.compose.material.icons.filled.Map import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Icon @@ -21,6 +22,7 @@ import co.touchlab.droidcon.ui.session.SessionListView import co.touchlab.droidcon.ui.settings.SettingsView import co.touchlab.droidcon.ui.sponsors.SponsorsView import co.touchlab.droidcon.ui.util.observeAsState +import co.touchlab.droidcon.ui.venue.VenueView import co.touchlab.droidcon.viewmodel.ApplicationViewModel @Composable @@ -34,7 +36,9 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod viewModel.tabs.forEach { tab -> val (title, icon) = when (tab) { ApplicationViewModel.Tab.Schedule -> "Schedule" to Icons.Filled.CalendarMonth - ApplicationViewModel.Tab.MyAgenda -> "My Agenda" to Icons.Filled.Schedule + // FIXME: Was originally "My agenda" but then it doesn't seem to fit. + ApplicationViewModel.Tab.MyAgenda -> "Agenda" to Icons.Filled.Schedule + ApplicationViewModel.Tab.Venue -> "Venue" to Icons.Filled.Map ApplicationViewModel.Tab.Sponsors -> "Sponsors" to Icons.Filled.LocalFireDepartment ApplicationViewModel.Tab.Settings -> "Settings" to Icons.Filled.Settings } @@ -48,17 +52,28 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod colors = NavigationBarItemDefaults.colors( indicatorColor = MaterialTheme.colorScheme.primary, selectedIconColor = MaterialTheme.colorScheme.onPrimary, - selectedTextColor = MaterialTheme.colorScheme.primary - ) + selectedTextColor = MaterialTheme.colorScheme.primary, + ), ) } } - } + }, ) { paddingValues -> Box(modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding())) { when (selectedTab) { - ApplicationViewModel.Tab.Schedule -> SessionListView(viewModel.schedule) - ApplicationViewModel.Tab.MyAgenda -> SessionListView(viewModel.agenda) + ApplicationViewModel.Tab.Schedule -> SessionListView( + viewModel = viewModel.schedule, + title = "Droidcon London 2024", + emptyText = "Sessions could not be loaded.", + ) + + ApplicationViewModel.Tab.MyAgenda -> SessionListView( + viewModel = viewModel.agenda, + title = "Agenda", + emptyText = "Add sessions to your agenda from session detail in schedule.", + ) + + ApplicationViewModel.Tab.Venue -> VenueView() ApplicationViewModel.Tab.Sponsors -> SponsorsView(viewModel.sponsors) ApplicationViewModel.Tab.Settings -> SettingsView(viewModel.settings) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/FeedbackDialog.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/FeedbackDialog.kt index 572c89287..c2ef5507e 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/FeedbackDialog.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/FeedbackDialog.kt @@ -41,7 +41,7 @@ import co.touchlab.droidcon.viewmodel.FeedbackDialogViewModel internal fun FeedbackDialog(feedback: FeedbackDialogViewModel) { Dialog(dismiss = feedback::skipTapped) { Card( - modifier = Modifier.padding(Dimensions.Padding.double) + modifier = Modifier.padding(Dimensions.Padding.double), ) { Column( modifier = Modifier @@ -86,7 +86,7 @@ internal fun FeedbackDialog(feedback: FeedbackDialogViewModel) { placeholder = { Text( text = "(Optional) Suggest improvement", - style = MaterialTheme.typography.bodyLarge + style = MaterialTheme.typography.bodyLarge, ) }, textStyle = MaterialTheme.typography.bodyLarge, @@ -113,7 +113,7 @@ internal fun FeedbackDialog(feedback: FeedbackDialogViewModel) { TextButton(onClick = feedback::closeAndDisableTapped) { Text( text = "CLOSE AND DISABLE FEEDBACK", - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) } TextButton(onClick = feedback::skipTapped) { diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/UiModule.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/UiModule.kt index 5e644fbbf..34ad42e00 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/UiModule.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/UiModule.kt @@ -32,6 +32,7 @@ val uiModule = module { feedbackDialogFactory = get(), syncService = get(), notificationSchedulingService = get(), + notificationService = get(), feedbackService = get(), settingsGateway = get(), ) @@ -78,6 +79,7 @@ val uiModule = module { settingsGateway = get(), feedbackDialogFactory = get(), feedbackService = get(), + notificationService = get(), ) } single { SpeakerListItemViewModel.Factory() } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionBlockView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionBlockView.kt index 3619c74a0..df0dcd47c 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionBlockView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionBlockView.kt @@ -65,7 +65,7 @@ internal fun SessionBlockView(sessionsBlock: SessionBlockViewModel) { modifier = Modifier.padding(Dimensions.Padding.half), fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.titleMedium + style = MaterialTheme.typography.titleMedium, ) session.room?.let { roomName -> Text( @@ -85,7 +85,7 @@ internal fun SessionBlockView(sessionsBlock: SessionBlockViewModel) { end = Dimensions.Padding.half, bottom = Dimensions.Padding.half, ), - style = MaterialTheme.typography.titleSmall + style = MaterialTheme.typography.titleSmall, ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionDetailView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionDetailView.kt index fd4825959..5ddcc4391 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionDetailView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionDetailView.kt @@ -37,7 +37,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight @@ -63,10 +62,10 @@ internal fun SessionDetailView(viewModel: SessionDetailViewModel) { NavigationStack( key = viewModel, links = { - NavigationLink(viewModel.observePresentedSpeakerDetail) { + navigationLink(viewModel.observePresentedSpeakerDetail) { SpeakerDetailView(viewModel = it) } - } + }, ) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( @@ -82,7 +81,7 @@ internal fun SessionDetailView(viewModel: SessionDetailViewModel) { ) } }, - scrollBehavior = scrollBehavior + scrollBehavior = scrollBehavior, ) }, ) { paddingValues -> @@ -95,7 +94,7 @@ internal fun SessionDetailView(viewModel: SessionDetailViewModel) { Column( modifier = Modifier .verticalScroll(scrollState) - .padding(top = paddingValues.calculateTopPadding()) + .padding(top = paddingValues.calculateTopPadding()), ) { val state by viewModel.observeState.observeAsState() Box(contentAlignment = Alignment.BottomStart) { @@ -112,8 +111,8 @@ internal fun SessionDetailView(viewModel: SessionDetailViewModel) { modifier = Modifier .padding(start = Dimensions.Padding.default) .size(44.dp), - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.secondary + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, ) { val icon = if (isAttending) Icons.Default.BookmarkAdded else Icons.Outlined.BookmarkAdd @@ -216,7 +215,7 @@ private fun HeaderView(title: String, locationInfo: String) { private fun InfoView(status: String) { Row( modifier = Modifier.fillMaxWidth().padding(top = Dimensions.Padding.default), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon( imageVector = Icons.Default.Info, @@ -253,6 +252,7 @@ private fun DescriptionView(description: String, links: List) { WebLinkText( text = description, links = links, + fontWeight = FontWeight.Normal, modifier = Modifier.padding( end = Dimensions.Padding.default, top = Dimensions.Padding.half, @@ -267,7 +267,7 @@ private fun SpeakerView(speaker: SpeakerListItemViewModel) { Column( modifier = Modifier .fillMaxWidth() - .clickable { speaker.selected() } + .clickable { speaker.selected() }, ) { Row(verticalAlignment = Alignment.CenterVertically) { val imageUrl = speaker.avatarUrl?.string @@ -280,7 +280,7 @@ private fun SpeakerView(speaker: SpeakerListItemViewModel) { .padding( start = Dimensions.Padding.default, end = Dimensions.Padding.default, - top = Dimensions.Padding.half + top = Dimensions.Padding.half, ) .clip(CircleShape) .aspectRatio(1f) @@ -290,7 +290,8 @@ private fun SpeakerView(speaker: SpeakerListItemViewModel) { Text( text = speaker.info, - color = Color.Gray, + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.Medium, modifier = Modifier.padding( end = Dimensions.Padding.default, top = Dimensions.Padding.half, @@ -300,6 +301,7 @@ private fun SpeakerView(speaker: SpeakerListItemViewModel) { } Text( text = speaker.bio ?: "", + fontWeight = FontWeight.Normal, modifier = Modifier.padding( start = 80.dp, end = Dimensions.Padding.default, diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt index 84653b340..dfa6d7274 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt @@ -1,7 +1,7 @@ package co.touchlab.droidcon.ui.session +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.interaction.DragInteraction import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -13,9 +13,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DateRange @@ -33,12 +34,12 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -57,24 +58,24 @@ import co.touchlab.droidcon.viewmodel.session.SessionDayViewModel import co.touchlab.kermit.Logger import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable -internal fun SessionListView(viewModel: BaseSessionListViewModel) { +internal fun SessionListView(viewModel: BaseSessionListViewModel, title: String, emptyText: String) { NavigationStack( key = viewModel, links = { - NavigationLink(viewModel.observePresentedSessionDetail) { + navigationLink(viewModel.observePresentedSessionDetail) { SessionDetailView(viewModel = it) } - } + }, ) { val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { CenterAlignedTopAppBar( - title = { Text("Droidcon London 2023") }, - scrollBehavior = scrollBehavior + title = { Text(title) }, + scrollBehavior = scrollBehavior, ) }, ) { paddingValues -> @@ -82,47 +83,50 @@ internal fun SessionListView(viewModel: BaseSessionListViewModel) { Column( modifier = Modifier .onSizeChanged { size = it } - .padding(top = paddingValues.calculateTopPadding()) + .padding(top = paddingValues.calculateTopPadding()), ) { val days by viewModel.observeDays.observeAsState() if (days?.isEmpty() != false) { - EmptyView() + EmptyView(emptyText) } else { val selectedDay by viewModel.observeSelectedDay.observeAsState() val selectedTabIndex = viewModel.days?.indexOf(selectedDay) ?: 0 - val state = rememberLazyListState(initialFirstVisibleItemIndex = selectedTabIndex) val coroutineScope = rememberCoroutineScope() - if (state.firstVisibleItemIndex != selectedTabIndex && !state.isScrollInProgress) { - coroutineScope.launch { - state.scrollToItem(selectedTabIndex) - } - } + val pagerState = rememberPagerState( + initialPage = selectedTabIndex, + pageCount = { + days?.size ?: 0 + }, + ) TabRow( - selectedTabIndex = selectedTabIndex, + selectedTabIndex = pagerState.currentPage, indicator = { tabPositions -> - if (tabPositions.indices.contains(selectedTabIndex)) { + if (tabPositions.indices.contains(pagerState.currentPage)) { TabIndicator( - Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]) + Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]), ) } else { - Logger.w("SessionList TabRow requested an indicator for selectedTabIndex: $selectedTabIndex, but only got ${tabPositions.count()} tabs.") - TabRowDefaults.Indicator() + Logger.w( + "SessionList TabRow requested an indicator for selectedTabIndex: " + + "${pagerState.currentPage}, but only got ${tabPositions.count()} tabs.", + ) + TabRowDefaults.SecondaryIndicator() } - } + }, ) { days?.forEachIndexed { index, daySchedule -> Tab( - selected = selectedTabIndex == index, + selected = pagerState.currentPage == index, onClick = { viewModel.selectedDay = daySchedule coroutineScope.launch { - state.animateScrollToItem(index) + pagerState.animateScrollToPage(index) } }, - unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant + unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant, ) { Text( text = daySchedule.day, @@ -133,48 +137,43 @@ internal fun SessionListView(viewModel: BaseSessionListViewModel) { } } - val interaction by state.interactionSource.interactions.collectAsState(null) - if (interaction is DragInteraction.Stop) { - val scrollToIndex = if (state.firstVisibleItemScrollOffset >= size.width / 2) { - state.firstVisibleItemIndex + 1 - } else { - state.firstVisibleItemIndex - } - LaunchedEffect(interaction) { - state.animateScrollToItem(scrollToIndex) + LaunchedEffect(pagerState) { + snapshotFlow { pagerState.currentPage }.collect { page -> + viewModel.selectedDay = viewModel.days?.get(page) } - viewModel.selectedDay = viewModel.days?.get(scrollToIndex) } + HorizontalPager( + state = pagerState, + ) { page -> + val day = days?.get(page) ?: return@HorizontalPager + val scrollState = rememberLazyListState( + day.scrollState.firstVisibleItemIndex, + day.scrollState.firstVisibleItemScrollOffset, + ) + if ( + day.scrollState.firstVisibleItemIndex != scrollState.firstVisibleItemIndex || + day.scrollState.firstVisibleItemScrollOffset != scrollState.firstVisibleItemScrollOffset + ) { + day.scrollState = SessionDayViewModel.ScrollState( + scrollState.firstVisibleItemIndex, + scrollState.firstVisibleItemScrollOffset, + ) + } - LazyRow(state = state) { - items(days ?: emptyList()) { day -> - val scrollState = - rememberLazyListState(day.scrollState.firstVisibleItemIndex, day.scrollState.firstVisibleItemScrollOffset) - if ( - day.scrollState.firstVisibleItemIndex != scrollState.firstVisibleItemIndex || - day.scrollState.firstVisibleItemScrollOffset != scrollState.firstVisibleItemScrollOffset - ) { - day.scrollState = SessionDayViewModel.ScrollState( - scrollState.firstVisibleItemIndex, - scrollState.firstVisibleItemScrollOffset - ) - } - - val density = LocalDensity.current - LazyColumn( - state = scrollState, - contentPadding = PaddingValues(vertical = Dimensions.Padding.quarter), - modifier = Modifier.width(with(density) { size.width.toDp() }), - ) { - items(day.blocks) { hourBlock -> - Box( - modifier = Modifier.padding( - vertical = Dimensions.Padding.quarter, - horizontal = Dimensions.Padding.half, - ), - ) { - SessionBlockView(hourBlock) - } + val density = LocalDensity.current + LazyColumn( + state = scrollState, + contentPadding = PaddingValues(vertical = Dimensions.Padding.quarter), + modifier = Modifier.width(with(density) { size.width.toDp() }), + ) { + items(day.blocks) { hourBlock -> + Box( + modifier = Modifier.padding( + vertical = Dimensions.Padding.quarter, + horizontal = Dimensions.Padding.half, + ), + ) { + SessionBlockView(hourBlock) } } } @@ -193,12 +192,12 @@ private fun TabIndicator(modifier: Modifier = Modifier) { .padding(horizontal = 64.dp) .height(2.dp) .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) - .background(MaterialTheme.colorScheme.primary) + .background(MaterialTheme.colorScheme.primary), ) } @Composable -private fun EmptyView() { +private fun EmptyView(text: String) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, @@ -214,7 +213,7 @@ private fun EmptyView() { ) Text( - text = "Sessions could not be loaded.", + text = text, modifier = Modifier.padding(Dimensions.Padding.default), textAlign = TextAlign.Center, ) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SpeakerDetailView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SpeakerDetailView.kt index bbdfbcab0..c16ad3dd0 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SpeakerDetailView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SpeakerDetailView.kt @@ -60,7 +60,7 @@ internal fun SpeakerDetailView(viewModel: SpeakerDetailViewModel) { ) } }, - scrollBehavior = scrollBehavior + scrollBehavior = scrollBehavior, ) }, ) { paddingValues -> @@ -104,7 +104,7 @@ private fun HeaderView(name: String, tagLine: String, imageUrl: Url?) { .width(100.dp) .padding(Dimensions.Padding.default) .clip(CircleShape) - .aspectRatio(1f) + .aspectRatio(1f), ) } @@ -140,7 +140,7 @@ private fun SocialView(url: WebLink, iconName: String) { contentDescription = null, modifier = Modifier .padding(Dimensions.Padding.default) - .size(28.dp) + .size(28.dp), ) WebLinkText( text = url.link, @@ -180,7 +180,7 @@ private fun SocialView(url: WebLink, icon: ImageVector) { private fun BioView(bio: String, webLinks: List) { Row( modifier = Modifier.fillMaxWidth().padding(vertical = Dimensions.Padding.half), - verticalAlignment = Alignment.Top + verticalAlignment = Alignment.Top, ) { Icon( imageVector = Icons.Default.Description, diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/PlatformSpecificSettings.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/PlatformSpecificSettings.kt index f84deb582..466225668 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/PlatformSpecificSettings.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/PlatformSpecificSettings.kt @@ -5,3 +5,6 @@ import co.touchlab.droidcon.viewmodel.settings.SettingsViewModel @Composable internal expect fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) + +@Composable +internal expect fun PlatformSwitchApp() diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt index b95b70f24..5a2bfb0e8 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt @@ -11,8 +11,8 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MailOutline import androidx.compose.material.icons.filled.Notifications -import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.dp +import co.touchlab.droidcon.Constants import co.touchlab.droidcon.ui.theme.Dimensions import co.touchlab.droidcon.ui.util.observeAsState import co.touchlab.droidcon.viewmodel.settings.SettingsViewModel @@ -41,7 +42,12 @@ internal fun SettingsView(viewModel: SettingsViewModel) { topBar = { TopAppBar( title = { Text("Settings") }, - scrollBehavior = scrollBehavior + scrollBehavior = scrollBehavior, + actions = { + if (Constants.SisterApp.showLaunchButton) { + PlatformSwitchApp() + } + }, ) }, ) { paddingValues -> @@ -50,7 +56,7 @@ internal fun SettingsView(viewModel: SettingsViewModel) { modifier = Modifier .padding(top = paddingValues.calculateTopPadding()) .fillMaxHeight() - .verticalScroll(scrollState) + .verticalScroll(scrollState), ) { IconTextSwitchRow( text = "Enable feedback", @@ -58,7 +64,7 @@ internal fun SettingsView(viewModel: SettingsViewModel) { checked = viewModel.observeIsFeedbackEnabled, ) - Divider() + HorizontalDivider() IconTextSwitchRow( text = "Enable reminders", @@ -66,7 +72,7 @@ internal fun SettingsView(viewModel: SettingsViewModel) { checked = viewModel.observeIsRemindersEnabled, ) - Divider() + HorizontalDivider() PlatformSpecificSettingsView(viewModel = viewModel) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorDetailView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorDetailView.kt index 2d965cf98..416f58f5d 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorDetailView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorDetailView.kt @@ -51,10 +51,10 @@ internal fun SponsorDetailView(viewModel: SponsorDetailViewModel) { NavigationStack( key = viewModel, links = { - NavigationLink(viewModel.observePresentedSpeakerDetail) { + navigationLink(viewModel.observePresentedSpeakerDetail) { SpeakerDetailView(viewModel = it) } - } + }, ) { Scaffold( topBar = { @@ -67,9 +67,9 @@ internal fun SponsorDetailView(viewModel: SponsorDetailViewModel) { contentDescription = "Back", ) } - } + }, ) - } + }, ) { paddingValues -> val scrollState = rememberScrollState() Column( @@ -80,7 +80,7 @@ internal fun SponsorDetailView(viewModel: SponsorDetailViewModel) { HeaderView( name = viewModel.name, groupTitle = viewModel.groupName, - imageUrl = viewModel.imageUrl + imageUrl = viewModel.imageUrl, ) viewModel.abstract?.let { @@ -195,7 +195,7 @@ private fun RepresentativeInfoView(profile: SpeakerListItemViewModel) { .padding( start = Dimensions.Padding.default, end = Dimensions.Padding.default, - top = Dimensions.Padding.half + top = Dimensions.Padding.half, ) .clip(CircleShape) .aspectRatio(1f) @@ -211,7 +211,7 @@ private fun RepresentativeInfoView(profile: SpeakerListItemViewModel) { .padding( start = Dimensions.Padding.default, end = Dimensions.Padding.default, - top = Dimensions.Padding.half + top = Dimensions.Padding.half, ) .clip(CircleShape) .aspectRatio(1f) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorsView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorsView.kt index e3c98e2b0..e1755a5d6 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorsView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/sponsors/SponsorsView.kt @@ -55,10 +55,10 @@ internal fun SponsorsView(viewModel: SponsorListViewModel) { NavigationStack( key = viewModel, links = { - NavigationLink(viewModel.observePresentedSponsorDetail) { + navigationLink(viewModel.observePresentedSponsorDetail) { SponsorDetailView(viewModel = it) } - } + }, ) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( @@ -66,7 +66,7 @@ internal fun SponsorsView(viewModel: SponsorListViewModel) { topBar = { TopAppBar( title = { Text("Sponsors") }, - scrollBehavior = scrollBehavior + scrollBehavior = scrollBehavior, ) }, ) { paddingValues -> @@ -99,8 +99,8 @@ private fun SponsorGroupView(sponsorGroup: SponsorGroupViewModel) { Card( modifier = Modifier.padding( vertical = Dimensions.Padding.quarter, - horizontal = Dimensions.Padding.half - ) + horizontal = Dimensions.Padding.half, + ), ) { Column(modifier = Modifier.fillMaxWidth()) { Text( diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Colors.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Colors.kt index 4c54800dd..40b62c3fd 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Colors.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Colors.kt @@ -4,9 +4,9 @@ import androidx.compose.ui.graphics.Color object Colors { - val primary = Color(0xFF7DE1C3) - val secondary = Color(0xFF0014E6) - val secondaryLighter = Color(0xFF0014E6) + val primary = Color(0xFF7de1c3) + val secondary = Color(0xFF0014e6) + val secondaryLighter = Color(0xFF0014e6) val imageBackground = Color(0xFF999999) val darkBlue = Color(0xFF0E65B1) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Theme.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Theme.kt index af7e87e9a..85aa1274a 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Theme.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Theme.kt @@ -7,7 +7,16 @@ import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable private val LightColorScheme = lightColorScheme( - primary = Colors.droidcon_theme_light_primary, + primary = Colors.primary, +) + +private val DarkColorScheme = darkColorScheme( + primary = Colors.primary, +) + +@Suppress("ktlint:standard:backing-property-naming") +private val _LightColorScheme = lightColorScheme( + primary = Colors.primary, onPrimary = Colors.droidcon_theme_light_onPrimary, primaryContainer = Colors.droidcon_theme_light_primaryContainer, onPrimaryContainer = Colors.droidcon_theme_light_onPrimaryContainer, @@ -38,7 +47,8 @@ private val LightColorScheme = lightColorScheme( onSurfaceVariant = Colors.droidcon_theme_light_onSurfaceVariant, ) -private val DarkColorScheme = darkColorScheme( +@Suppress("ktlint:standard:backing-property-naming") +private val _DarkColorScheme = darkColorScheme( primary = Colors.droidcon_theme_dark_primary, onPrimary = Colors.droidcon_theme_dark_onPrimary, primaryContainer = Colors.droidcon_theme_dark_primaryContainer, @@ -71,10 +81,7 @@ private val DarkColorScheme = darkColorScheme( ) @Composable -internal fun DroidconTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit, -) { +internal fun DroidconTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colorScheme = when { darkTheme -> DarkColorScheme else -> LightColorScheme diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Typography.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Typography.kt index 82ab85158..4279960a5 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Typography.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/theme/Typography.kt @@ -10,64 +10,64 @@ internal object Typography { headlineLarge = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 32.sp + fontSize = 32.sp, ), headlineMedium = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 28.sp + fontSize = 28.sp, ), headlineSmall = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, fontSize = 22.sp, - letterSpacing = 0.15.sp + letterSpacing = 0.15.sp, ), titleLarge = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 20.sp + fontSize = 20.sp, ), titleMedium = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 16.sp + fontSize = 16.sp, ), titleSmall = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 14.sp + fontSize = 14.sp, ), bodyLarge = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 16.sp + fontSize = 16.sp, ), bodyMedium = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 14.sp + fontSize = 14.sp, ), bodySmall = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 12.sp + fontSize = 12.sp, ), labelLarge = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.SemiBold, - fontSize = 12.sp + fontSize = 12.sp, ), labelMedium = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, - fontSize = 12.sp + fontSize = 12.sp, ), labelSmall = TextStyle( fontFamily = montserratFontFamily, fontWeight = FontWeight.Medium, fontSize = 10.sp, - letterSpacing = 1.5.sp - ) + letterSpacing = 1.5.sp, + ), ) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/Image.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/Image.kt index 117aa3cbc..197c04ecf 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/Image.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/Image.kt @@ -10,12 +10,7 @@ import coil3.request.crossfade import coil3.util.DebugLogger @Composable -fun DcAsyncImage( - logTag: String, - model: Any?, - contentDescription: String?, - modifier: Modifier = Modifier, -) { +fun DcAsyncImage(logTag: String, model: Any?, contentDescription: String?, modifier: Modifier = Modifier) { AsyncImage( modifier = modifier, model = model, @@ -24,22 +19,17 @@ fun DcAsyncImage( Logger.e( messageString = logTag, throwable = it.result.throwable, - tag = "AsyncImage OnError Request = ${it.result.request}\n" + tag = "AsyncImage OnError Request = ${it.result.request}\n", ) }, ) } -fun dcImageLoader( - context: PlatformContext, - debug: Boolean = false, -): ImageLoader { - return ImageLoader.Builder(context) - .crossfade(true) - .apply { - if (debug) { - logger(DebugLogger()) - } +fun dcImageLoader(context: PlatformContext, debug: Boolean = false): ImageLoader = ImageLoader.Builder(context) + .crossfade(true) + .apply { + if (debug) { + logger(DebugLogger()) } - .build() -} + } + .build() diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/venue/VenueView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/venue/VenueView.kt new file mode 100644 index 000000000..475b00262 --- /dev/null +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/venue/VenueView.kt @@ -0,0 +1,28 @@ +package co.touchlab.droidcon.ui.venue + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.github.panpf.zoomimage.ZoomImage +import droidcon.shared_ui.generated.resources.venue_map_1 +import org.jetbrains.compose.resources.painterResource + +@Composable +fun VenueView() { + Scaffold { paddingValues -> + VenueBodyView( + modifier = Modifier.padding(paddingValues), + ) + } +} + +@Composable +fun VenueBodyView(modifier: Modifier = Modifier) { + ZoomImage( + painter = painterResource(droidcon.shared_ui.generated.resources.Res.drawable.venue_map_1), + contentDescription = null, + modifier = modifier.fillMaxSize(), + ) +} diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/LocalDateTime+startOfMinute.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/LocalDateTime+startOfMinute.kt index ca0f31916..00317ab1a 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/LocalDateTime+startOfMinute.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/LocalDateTime+startOfMinute.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:filename") + package co.touchlab.droidcon.util import kotlinx.datetime.LocalDateTime diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt index 76f94f926..66eaa4042 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt @@ -48,16 +48,13 @@ class NavigationController : BaseViewModel() { internal sealed class NavigationStackItem { class BackPressHandler(val onBackPressed: BackPressHandlerScope.() -> Unit) : NavigationStackItem() { - override fun toString(): String { - return "BackPress@${hashCode().toUInt().toString(16)}" - } + override fun toString(): String = "BackPress@${hashCode().toUInt().toString(16)}" } class Push(val item: MutableObservableProperty, val content: @Composable (T) -> Unit) : NavigationStackItem() { - override fun toString(): String { - return "Push(${item.value}@${item.hashCode().toUInt().toString(16)})@${hashCode().toUInt().toString(16)}" - } + override fun toString(): String = + "Push(${item.value}@${item.hashCode().toUInt().toString(16)})@${hashCode().toUInt().toString(16)}" } } @@ -176,9 +173,7 @@ class NavigationController : BaseViewModel() { } } -internal data class NavigationViewDimensions( - val constraints: Constraints, -) +internal data class NavigationViewDimensions(val constraints: Constraints) @Composable internal fun rememberNavigationController(): NavigationController = remember { @@ -213,7 +208,7 @@ internal fun BackPressHandler(onBackPressed: NavigationController.BackPressHandl internal interface NavigationStackScope { - fun NavigationLink(item: MutableObservableProperty, content: @Composable (T) -> Unit) + fun navigationLink(item: MutableObservableProperty, content: @Composable (T) -> Unit) } internal class NavigationLinkWrapper( @@ -235,13 +230,11 @@ internal class NavigationLinkWrapper( } } - override fun equals(other: Any?): Boolean { - return (other as? NavigationLinkWrapper<*>)?.let { it.index == index && it.value == value } ?: false - } + override fun equals(other: Any?): Boolean = (other as? NavigationLinkWrapper<*>)?.let { + it.index == index && it.value == value + } ?: false - override fun hashCode(): Int { - return listOfNotNull(index, value).hashCode() - } + override fun hashCode(): Int = listOfNotNull(index, value).hashCode() } @OptIn(ExperimentalAnimationApi::class) @@ -250,14 +243,11 @@ internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, val activeLinkComposables by remember(key) { val constructedLinks = mutableListOf>>() val scope = object : NavigationStackScope { - override fun NavigationLink( - item: MutableObservableProperty, - content: @Composable (T) -> Unit, - ) { + override fun navigationLink(item: MutableObservableProperty, content: @Composable (T) -> Unit) { constructedLinks.add( item.map { NavigationLinkWrapper(index = constructedLinks.size, value = it, reset = { item.value = null }, content) - } + }, ) } } @@ -275,7 +265,7 @@ internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, slideInHorizontally(initialOffsetX = { -it }) with slideOutHorizontally(targetOffsetX = { it }) } }, - contentAlignment = Alignment.BottomCenter + contentAlignment = Alignment.BottomCenter, ) { activeComposables -> SubcomposeLayout( measurePolicy = { constraints -> @@ -297,7 +287,7 @@ internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, it.measure(looseConstraints).place(x = 0, y = 0) } } - } + }, ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/ApplicationViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/ApplicationViewModel.kt index b50ddef64..fdd3ae680 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/ApplicationViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/ApplicationViewModel.kt @@ -1,11 +1,13 @@ package co.touchlab.droidcon.viewmodel +import co.touchlab.droidcon.Constants import co.touchlab.droidcon.application.gateway.SettingsGateway +import co.touchlab.droidcon.application.service.Notification import co.touchlab.droidcon.application.service.NotificationSchedulingService import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.service.FeedbackService import co.touchlab.droidcon.domain.service.SyncService -import co.touchlab.droidcon.service.NotificationHandler +import co.touchlab.droidcon.service.DeepLinkNotificationHandler import co.touchlab.droidcon.viewmodel.session.AgendaViewModel import co.touchlab.droidcon.viewmodel.session.ScheduleViewModel import co.touchlab.droidcon.viewmodel.settings.SettingsViewModel @@ -21,9 +23,11 @@ class ApplicationViewModel( private val feedbackDialogFactory: FeedbackDialogViewModel.Factory, private val syncService: SyncService, private val notificationSchedulingService: NotificationSchedulingService, + private val notificationService: NotificationService, private val feedbackService: FeedbackService, private val settingsGateway: SettingsGateway, -) : BaseViewModel(), NotificationHandler { +) : BaseViewModel(), + DeepLinkNotificationHandler { val schedule by managed(scheduleFactory.create()) val agenda by managed(agendaFactory.create()) @@ -38,13 +42,19 @@ class ApplicationViewModel( instanceLock.runExclusively { settingsGateway.setUseComposeForIos(newValue) } - } + }, ) var presentedFeedback: FeedbackDialogViewModel? by managed(null) val observePresentedFeedback by observe(::presentedFeedback) - val tabs = listOf(Tab.Schedule, Tab.MyAgenda, Tab.Sponsors, Tab.Settings) + val tabs = listOfNotNull( + Tab.Schedule, + Tab.MyAgenda, + if (Constants.showVenueMap) Tab.Venue else null, + Tab.Sponsors, + Tab.Settings, + ) var selectedTab: Tab by published(Tab.Schedule) val observeSelectedTab by observe(::selectedTab) @@ -60,16 +70,17 @@ class ApplicationViewModel( } } - override fun notificationReceived(sessionId: String, notificationType: NotificationService.NotificationType) { - when (notificationType) { - NotificationService.NotificationType.Feedback -> + override fun handleDeepLinkNotification(notification: Notification.DeepLink) { + when (notification) { + is Notification.Local.Feedback -> lifecycle.whileAttached { // We're not checking whether feedback is enabled, because the user opened a feedback notification. presentNextFeedback() } - NotificationService.NotificationType.Reminder -> { + + is Notification.Local.Reminder -> { selectedTab = Tab.Schedule - schedule.openSessionDetail(sessionId) + schedule.openSessionDetail(notification.sessionId) } } } @@ -88,6 +99,7 @@ class ApplicationViewModel( session, submit = { feedback -> feedbackService.submit(session, feedback) + notificationService.cancel(listOf(session.id)) presentNextFeedback() }, closeAndDisable = { @@ -103,6 +115,10 @@ class ApplicationViewModel( } enum class Tab { - Schedule, MyAgenda, Sponsors, Settings; + Schedule, + MyAgenda, + Venue, + Sponsors, + Settings, } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/FeedbackDialogViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/FeedbackDialogViewModel.kt index 6d5503630..4254cc1d6 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/FeedbackDialogViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/FeedbackDialogViewModel.kt @@ -38,19 +38,21 @@ class FeedbackDialogViewModel( fun skipTapped() = instanceLock.runExclusively(skip::invoke) - private fun feedbackRatingToRating(rating: Int): Rating? = - when (rating) { - Session.Feedback.Rating.DISSATISFIED -> Rating.Dissatisfied - Session.Feedback.Rating.NORMAL -> Rating.Normal - Session.Feedback.Rating.SATISFIED -> Rating.Satisfied - else -> { - log.w("Unknown feedback rating $rating.") - null - } + private fun feedbackRatingToRating(rating: Int): Rating? = when (rating) { + Session.Feedback.Rating.DISSATISFIED -> Rating.Dissatisfied + Session.Feedback.Rating.NORMAL -> Rating.Normal + Session.Feedback.Rating.SATISFIED -> Rating.Satisfied + else -> { + log.w("Unknown feedback rating $rating.") + null } + } enum class Rating { - Dissatisfied, Normal, Satisfied; + Dissatisfied, + Normal, + Satisfied, + ; val entityValue: Int get() = when (this) { @@ -60,10 +62,7 @@ class FeedbackDialogViewModel( } } - class Factory( - private val sessionGateway: SessionGateway, - private val log: Logger, - ) { + class Factory(private val sessionGateway: SessionGateway, private val log: Logger) { fun create( session: Session, diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt index 565f2c60c..ed46896a8 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt @@ -19,9 +19,9 @@ class ScheduleViewModel( attendingOnly = false, ) { - fun openSessionDetail(sessionId: String) { + fun openSessionDetail(sessionId: Session.Id) { lifecycle.whileAttached { - val sessionItem = sessionGateway.getScheduleItem(Session.Id(sessionId)) ?: return@whileAttached + val sessionItem = sessionGateway.getScheduleItem(sessionId) ?: return@whileAttached presentedSessionDetail = sessionDetailFactory.create(sessionItem) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt index f97da9c53..79434eb3f 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt @@ -19,19 +19,13 @@ class SessionBlockViewModel( item, selected = { onScheduleItemSelected(item) - } + }, ) - } + }, ) - class Factory( - private val sessionListItemFactory: SessionListItemViewModel.Factory, - private val dateFormatter: DateFormatter, - ) { - fun create( - startsAt: LocalDateTime, - items: List, - onScheduleItemSelected: (ScheduleItem) -> Unit, - ) = SessionBlockViewModel(sessionListItemFactory, dateFormatter, startsAt, items, onScheduleItemSelected) + class Factory(private val sessionListItemFactory: SessionListItemViewModel.Factory, private val dateFormatter: DateFormatter) { + fun create(startsAt: LocalDateTime, items: List, onScheduleItemSelected: (ScheduleItem) -> Unit) = + SessionBlockViewModel(sessionListItemFactory, dateFormatter, startsAt, items, onScheduleItemSelected) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDayViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDayViewModel.kt index 89ed6833d..7c9ee9fd0 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDayViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDayViewModel.kt @@ -25,7 +25,7 @@ class SessionDayViewModel( .groupBy { it.session.startsAt.toConferenceDateTime(dateTimeService).startOfMinute } .map { (startsAt, items) -> sessionBlockFactory.create(startsAt, items, onScheduleItemSelected) - } + }, ) var scrollState: ScrollState @@ -43,20 +43,16 @@ class SessionDayViewModel( private val sessionDetailScrollStateStorage: SessionDetailScrollStateStorage, ) { - fun create( - date: LocalDate, - attendingOnly: Boolean, - items: List, - onScheduleItemSelected: (ScheduleItem) -> Unit, - ) = SessionDayViewModel( - sessionBlockFactory, - dateFormatter, - dateTimeService, - date, - attendingOnly, - sessionDetailScrollStateStorage, - items, - onScheduleItemSelected, - ) + fun create(date: LocalDate, attendingOnly: Boolean, items: List, onScheduleItemSelected: (ScheduleItem) -> Unit) = + SessionDayViewModel( + sessionBlockFactory, + dateFormatter, + dateTimeService, + date, + attendingOnly, + sessionDetailScrollStateStorage, + items, + onScheduleItemSelected, + ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt index ba1ce1cae..b1c8e3bb7 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt @@ -8,12 +8,11 @@ class SessionDetailScrollStateStorage { val agendaScrollStates: MutableMap = mutableMapOf() var selectedDay: LocalDate? = null - fun getScrollState(day: LocalDate, agenda: Boolean): SessionDayViewModel.ScrollState = - if (agenda) { - agendaScrollStates[day] - } else { - scrollStates[day] - } ?: SessionDayViewModel.ScrollState(firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0) + fun getScrollState(day: LocalDate, agenda: Boolean): SessionDayViewModel.ScrollState = if (agenda) { + agendaScrollStates[day] + } else { + scrollStates[day] + } ?: SessionDayViewModel.ScrollState(firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0) fun setScrollState(day: LocalDate, agenda: Boolean, scrollState: SessionDayViewModel.ScrollState) { if (agenda) { diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt index bc1d12685..10c9e56f9 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt @@ -1,6 +1,7 @@ package co.touchlab.droidcon.viewmodel.session import co.touchlab.droidcon.application.gateway.SettingsGateway +import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.composite.ScheduleItem import co.touchlab.droidcon.domain.gateway.SessionGateway import co.touchlab.droidcon.domain.service.DateTimeService @@ -31,6 +32,7 @@ class SessionDetailViewModel( private val dateTimeService: DateTimeService, private val parseUrlViewService: ParseUrlViewService, private val feedbackService: FeedbackService, + private val notificationService: NotificationService, initialItem: ScheduleItem, ) : BaseViewModel() { @@ -44,7 +46,7 @@ class SessionDetailViewModel( emit(dateTimeService.now()) delay(10_000) } - } + }, ) private val observeTime by observe(::time) @@ -86,10 +88,10 @@ class SessionDetailViewModel( speaker, selected = { presentedSpeakerDetail = speakerDetailFactory.create(speaker) - } + }, ) } - } + }, ) val observeSpeakers by observe(::speakers) @@ -111,7 +113,7 @@ class SessionDetailViewModel( .map { it.isFeedbackEnabled } .combine(observeState.asFlow()) { feedbackEnabled, state -> feedbackEnabled && state == SessionState.Ended - } + }, ) val observeShowFeedbackOption by observe(::showFeedbackOption) @@ -126,6 +128,7 @@ class SessionDetailViewModel( item.session, submit = { feedback -> feedbackService.submit(item.session, feedback) + notificationService.cancel(listOf(item.session.id)) presentedFeedback = null }, closeAndDisable = null, @@ -143,7 +146,9 @@ class SessionDetailViewModel( } enum class SessionState { - InConflict, InProgress, Ended + InConflict, + InProgress, + Ended, } class Factory( @@ -156,11 +161,10 @@ class SessionDetailViewModel( private val dateTimeService: DateTimeService, private val parseUrlViewService: ParseUrlViewService, private val feedbackService: FeedbackService, + private val notificationService: NotificationService, ) { - fun create( - item: ScheduleItem, - ) = SessionDetailViewModel( + fun create(item: ScheduleItem) = SessionDetailViewModel( sessionGateway, settingsGateway, speakerListItemFactory, @@ -170,6 +174,7 @@ class SessionDetailViewModel( dateTimeService, parseUrlViewService, feedbackService, + notificationService, item, ) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionListItemViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionListItemViewModel.kt index f9b6cf8a5..dd0749753 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionListItemViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionListItemViewModel.kt @@ -6,11 +6,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class SessionListItemViewModel( - dateTimeService: DateTimeService, - item: ScheduleItem, - val selected: () -> Unit, -) : BaseViewModel() { +class SessionListItemViewModel(dateTimeService: DateTimeService, item: ScheduleItem, val selected: () -> Unit) : BaseViewModel() { val title: String = item.session.title val isServiceSession: Boolean = item.session.isServiceSession val isAttending: Boolean = item.session.rsvp.isAttending @@ -26,16 +22,11 @@ class SessionListItemViewModel( emit(isInPast) delay(10_000) } - } + }, ) val observeIsInPast by observe(::isInPast) - class Factory( - private val dateTimeService: DateTimeService, - ) { - fun create( - item: ScheduleItem, - selected: () -> Unit, - ) = SessionListItemViewModel(dateTimeService, item, selected) + class Factory(private val dateTimeService: DateTimeService) { + fun create(item: ScheduleItem, selected: () -> Unit) = SessionListItemViewModel(dateTimeService, item, selected) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SpeakerDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SpeakerDetailViewModel.kt index 75e38d076..e571ddb40 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SpeakerDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SpeakerDetailViewModel.kt @@ -6,10 +6,7 @@ import co.touchlab.droidcon.dto.WebLink import co.touchlab.droidcon.service.ParseUrlViewService import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class SpeakerDetailViewModel( - private val parseUrlViewService: ParseUrlViewService, - profile: Profile, -) : BaseViewModel() { +class SpeakerDetailViewModel(private val parseUrlViewService: ParseUrlViewService, profile: Profile) : BaseViewModel() { val avatarUrl = profile.profilePicture @@ -25,11 +22,7 @@ class SpeakerDetailViewModel( val bio = profile.bio val bioWebLinks: List = bio?.let(parseUrlViewService::parse) ?: emptyList() - data class Socials( - val website: Url?, - val twitter: Url?, - val linkedIn: Url?, - ) { + data class Socials(val website: Url?, val twitter: Url?, val linkedIn: Url?) { val isEmpty: Boolean = listOfNotNull( website, diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutItemViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutItemViewModel.kt index 01bfc1abb..ede6717c8 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutItemViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutItemViewModel.kt @@ -3,9 +3,4 @@ package co.touchlab.droidcon.viewmodel.settings import co.touchlab.droidcon.dto.WebLink import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class AboutItemViewModel( - val title: String, - val detail: String, - val webLinks: List, - val icon: String, -) : BaseViewModel() +class AboutItemViewModel(val title: String, val detail: String, val webLinks: List, val icon: String) : BaseViewModel() diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt index 14c092e08..ffc991025 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt @@ -5,10 +5,7 @@ import co.touchlab.droidcon.application.repository.AboutRepository import co.touchlab.droidcon.service.ParseUrlViewService import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class AboutViewModel( - private val aboutRepository: AboutRepository, - private val parseUrlViewService: ParseUrlViewService, -) : BaseViewModel() { +class AboutViewModel(private val aboutRepository: AboutRepository, private val parseUrlViewService: ParseUrlViewService) : BaseViewModel() { var items: List by published(emptyList()) private set diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/SettingsViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/SettingsViewModel.kt index 0507acaa2..cca11382a 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/SettingsViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/SettingsViewModel.kt @@ -3,10 +3,7 @@ package co.touchlab.droidcon.viewmodel.settings import co.touchlab.droidcon.application.gateway.SettingsGateway import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class SettingsViewModel( - settingsGateway: SettingsGateway, - private val aboutFactory: AboutViewModel.Factory, -) : BaseViewModel() { +class SettingsViewModel(settingsGateway: SettingsGateway, private val aboutFactory: AboutViewModel.Factory) : BaseViewModel() { var isFeedbackEnabled by binding( settingsGateway.settings(), @@ -16,7 +13,7 @@ class SettingsViewModel( instanceLock.runExclusively { settingsGateway.setFeedbackEnabled(newValue) } - } + }, ) val observeIsFeedbackEnabled by observe(::isFeedbackEnabled) @@ -28,7 +25,7 @@ class SettingsViewModel( instanceLock.runExclusively { settingsGateway.setRemindersEnabled(newValue) } - } + }, ) val observeIsRemindersEnabled by observe(::isRemindersEnabled) @@ -42,14 +39,11 @@ class SettingsViewModel( instanceLock.runExclusively { settingsGateway.setUseComposeForIos(newValue) } - } + }, ) val observeUseCompose by observe(::useCompose) - class Factory( - private val settingsGateway: SettingsGateway, - private val aboutFactory: AboutViewModel.Factory, - ) { + class Factory(private val settingsGateway: SettingsGateway, private val aboutFactory: AboutViewModel.Factory) { fun create() = SettingsViewModel(settingsGateway, aboutFactory) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt index b313f94cf..334f32d9f 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt @@ -32,7 +32,7 @@ class SponsorDetailViewModel( speaker, selected = { presentedSpeakerDetail = speakerDetailFactory.create(speaker) - } + }, ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupItemViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupItemViewModel.kt index aec1adae7..f5514181d 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupItemViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupItemViewModel.kt @@ -5,10 +5,7 @@ import io.ktor.http.URLParserException import io.ktor.http.Url import org.brightify.hyperdrive.multiplatformx.BaseViewModel -class SponsorGroupItemViewModel( - private val sponsor: Sponsor, - val selected: () -> Unit, -) : BaseViewModel() { +class SponsorGroupItemViewModel(private val sponsor: Sponsor, val selected: () -> Unit) : BaseViewModel() { val name = sponsor.name val imageUrl = sponsor.icon @@ -22,7 +19,6 @@ class SponsorGroupItemViewModel( class Factory { - fun create(sponsor: Sponsor, selected: () -> Unit) = - SponsorGroupItemViewModel(sponsor, selected) + fun create(sponsor: Sponsor, selected: () -> Unit) = SponsorGroupItemViewModel(sponsor, selected) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupViewModel.kt index b7740c318..2131db070 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorGroupViewModel.kt @@ -7,23 +7,19 @@ import org.brightify.hyperdrive.multiplatformx.BaseViewModel class SponsorGroupViewModel( sponsorGroupItemFactory: SponsorGroupItemViewModel.Factory, sponsorGroup: SponsorGroupWithSponsors, - onSponsorSelected: (Sponsor) -> Unit + onSponsorSelected: (Sponsor) -> Unit, ) : BaseViewModel() { val title = sponsorGroup.group.name val isProminent = sponsorGroup.group.isProminent val sponsors by managedList( sponsorGroup.sponsors.map { sponsor -> sponsorGroupItemFactory.create(sponsor, selected = { onSponsorSelected(sponsor) }) - } + }, ) val observeSponsors by observe(::sponsors) - class Factory( - private val sponsorGroupItemFactory: SponsorGroupItemViewModel.Factory, - ) { - fun create( - sponsorGroup: SponsorGroupWithSponsors, - onSponsorSelected: (Sponsor) -> Unit, - ) = SponsorGroupViewModel(sponsorGroupItemFactory, sponsorGroup, onSponsorSelected) + class Factory(private val sponsorGroupItemFactory: SponsorGroupItemViewModel.Factory) { + fun create(sponsorGroup: SponsorGroupWithSponsors, onSponsorSelected: (Sponsor) -> Unit) = + SponsorGroupViewModel(sponsorGroupItemFactory, sponsorGroup, onSponsorSelected) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt index 067eda996..46e95145b 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt @@ -26,10 +26,10 @@ class SponsorListViewModel( // UIApplication.sharedApplication.openURL(NSURL(string = sponsor.url.string)) presentedUrl = sponsor.url } - } + }, ) } - } + }, ) val observeSponsorGroups by observe(::sponsorGroups) diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/ComposeRootController.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/ComposeRootController.kt index ea984965b..92cb70d7f 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/ComposeRootController.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/ComposeRootController.kt @@ -1,8 +1,15 @@ package co.touchlab.droidcon.ui import androidx.compose.ui.window.ComposeUIViewController +import co.touchlab.droidcon.ui.venue.VenueBodyView import co.touchlab.droidcon.viewmodel.ApplicationViewModel +@Suppress("unused") fun getRootController(viewModel: ApplicationViewModel) = ComposeUIViewController { MainComposeView(viewModel) } + +@Suppress("unused") +fun venueBodyViewController() = ComposeUIViewController { + VenueBodyView() +} diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt index 88e5132be..f70571d5f 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt @@ -2,9 +2,14 @@ package co.touchlab.droidcon.ui.settings import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Aod -import androidx.compose.material3.Divider +import androidx.compose.material3.Button +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import co.touchlab.droidcon.Constants import co.touchlab.droidcon.viewmodel.settings.SettingsViewModel +import platform.Foundation.NSURL +import platform.UIKit.UIApplication @Composable internal actual fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) { @@ -14,5 +19,26 @@ internal actual fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) { checked = viewModel.observeUseCompose, ) - Divider() + HorizontalDivider() +} + +@Composable +internal actual fun PlatformSwitchApp() { + Button( + onClick = { + openSisterApp() + }, + ) { + Text("Open ${Constants.SisterApp.name}") + } +} + +fun openSisterApp() { + val url = NSURL(string = Constants.SisterApp.iosUrlString) + if (UIApplication.sharedApplication.canOpenURL(url)) { + UIApplication.sharedApplication.openURL(url) + } else { + val appStoreUrl = NSURL(string = Constants.SisterApp.iosAppStoreUrlString) + UIApplication.sharedApplication.openURL(appStoreUrl) + } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt index 0c6649d7b..1dbfb1b97 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt @@ -56,7 +56,7 @@ internal actual fun NavigationBackPressWrapper(content: @Composable () -> Unit) AnimatedVisibility( visible = dragDistance > triggerBackPressDragDistance, enter = slideInHorizontally(initialOffsetX = { -it }, animationSpec = tween(durationMillis = 100)), - exit = slideOutHorizontally(targetOffsetX = { -it }) + exit = slideOutHorizontally(targetOffsetX = { -it }), ) { Icon( imageVector = Icons.Default.KeyboardArrowLeft, diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt index 8aa2044f1..167c79d6a 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt @@ -37,7 +37,9 @@ internal fun UIImage.toSkiaImage(): Image? { val alphaType = when (alphaInfo) { CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL CGImageAlphaInfo.kCGImageAlphaFirst, CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL - CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE + CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast, + -> ColorAlphaType.OPAQUE + else -> ColorAlphaType.UNKNOWN } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 147771f87..bdaf03885 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -30,7 +30,7 @@ android { listOf( "src/androidMain/resources", "src/commonMain/resources", - ) + ), ) main.manifest.srcFile("src/androidMain/AndroidManifest.xml") } diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt index 2fffd36c6..196c70547 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt @@ -27,15 +27,19 @@ actual val platformModule: Module = module { OkHttp.create {} } - single { + single { AndroidNotificationService( context = get(), entrypointActivity = get(), log = getWith("AndroidNotificationService"), + syncService = get(), settings = get(), json = get(), ) } + single { + get() + } single { AndroidDateFormatter(dateTimeService = get()) diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.android.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.android.kt index 208ddbbbc..ec752fdd9 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.android.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.android.kt @@ -5,10 +5,6 @@ import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver import co.touchlab.droidcon.db.DroidconDatabase -actual class SqlDelightDriverFactory( - private val context: Context, -) { - actual fun createDriver(): SqlDriver { - return AndroidSqliteDriver(DroidconDatabase.Schema, context, "new-droidcon2023.db") - } +actual class SqlDelightDriverFactory(private val context: Context) { + actual fun createDriver(): SqlDriver = AndroidSqliteDriver(DroidconDatabase.Schema, context, "new-droidcon2024.db") } diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt index 3f03a14e3..eb4ac2d13 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt @@ -10,12 +10,13 @@ import android.content.Intent import android.os.Build import android.os.RemoteException import androidx.core.app.NotificationCompat +import co.touchlab.droidcon.application.service.Notification import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.entity.Session +import co.touchlab.droidcon.domain.service.SyncService import co.touchlab.droidcon.shared.R import co.touchlab.droidcon.util.IdentifiableIntent import co.touchlab.kermit.Logger -import com.russhwolf.settings.ExperimentalSettingsApi import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.get import com.russhwolf.settings.set @@ -23,11 +24,11 @@ import kotlinx.datetime.Instant import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -@OptIn(ExperimentalSettingsApi::class) class AndroidNotificationService( private val context: Context, private val entrypointActivity: Class, private val log: Logger, + private val syncService: SyncService, private val settings: ObservableSettings, private val json: Json, ) : NotificationService { @@ -43,9 +44,9 @@ class AndroidNotificationService( } ?: mutableMapOf() // TODO: Not called on Android. - private var notificationHandler: NotificationHandler? = null + private var notificationHandler: DeepLinkNotificationHandler? = null - override fun setHandler(notificationHandler: NotificationHandler) { + override fun setHandler(notificationHandler: DeepLinkNotificationHandler) { this.notificationHandler = notificationHandler } @@ -71,7 +72,7 @@ class AndroidNotificationService( return true } - override suspend fun schedule(type: NotificationService.NotificationType, sessionId: Session.Id, title: String, body: String, delivery: Instant, dismiss: Instant?) { + override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { log.v { "Scheduling local notification at $delivery." } val deliveryTime = delivery.toEpochMilliseconds() @@ -84,25 +85,34 @@ class AndroidNotificationService( .setCategory(NotificationCompat.CATEGORY_REMINDER) .setAutoCancel(true) + val requestCode = when (notification) { + is Notification.Local.Feedback -> NOTIFICATION_FEEDBACK_REQUEST_CODE + is Notification.Local.Reminder -> 0 + } + + val sessionId = when (notification) { + is Notification.Local.Feedback -> notification.sessionId + is Notification.Local.Reminder -> notification.sessionId + } + + val typeValue = when (notification) { + is Notification.Local.Feedback -> Notification.Values.FEEDBACK_TYPE + is Notification.Local.Reminder -> Notification.Values.REMINDER_TYPE + } + val contentIntent = PendingIntent.getActivity( context, - if (type == NotificationService.NotificationType.Feedback) NOTIFICATION_FEEDBACK_REQUEST_CODE else 0, + requestCode, Intent(context, entrypointActivity).apply { - putExtra(NOTIFICATION_SESSION_ID_EXTRA_KEY, sessionId.value) - putExtra( - NOTIFICATION_TYPE_EXTRA_KEY, - when (type) { - NotificationService.NotificationType.Reminder -> NOTIFICATION_TYPE_EXTRA_REMINDER - NotificationService.NotificationType.Feedback -> NOTIFICATION_TYPE_EXTRA_FEEDBACK - } - ) + putExtra(Notification.Keys.SESSION_ID, sessionId.value) + putExtra(Notification.Keys.NOTIFICATION_TYPE, typeValue) flags = Intent.FLAG_ACTIVITY_CLEAR_TOP }, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT - } + }, ) builder.setContentIntent(contentIntent) @@ -131,7 +141,9 @@ class AndroidNotificationService( } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { return } + if (sessionIds.isEmpty()) { + return + } log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } @@ -152,6 +164,62 @@ class AndroidNotificationService( } } + suspend fun handleNotificationDeeplink(intent: Intent) { + val notification = intent.parseNotification() ?: return + + handleNotification(notification) + } + + suspend fun handleNotification(notification: Notification) { + when (notification) { + is Notification.DeepLink -> { + val notificationHandler = notificationHandler + if (notificationHandler != null) { + notificationHandler.handleDeepLinkNotification(notification) + } else { + log.w { "notificationHandler not registered when received $notification" } + } + } + + Notification.Remote.RefreshData -> syncService.forceSynchronize() + } + } + + private fun Intent.parseNotification(): Notification? = + when (val typeValue = this.getStringExtra(Notification.Keys.NOTIFICATION_TYPE)) { + Notification.Values.REMINDER_TYPE -> this.parseReminderNotification() + Notification.Values.FEEDBACK_TYPE -> this.parseFeedbackNotification() + Notification.Values.REFRESH_DATA_TYPE -> Notification.Remote.RefreshData + // Expected on Android as this could've been just a regular app open without a notification. + null -> null + else -> { + log.e { "Unknown notification type <$typeValue>, ignoring." } + null + } + } + + private fun Intent.parseReminderNotification(): Notification.Local.Reminder? { + val sessionId = this.getStringExtra(Notification.Keys.SESSION_ID) ?: run { + log.e { "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." } + return null + } + + return Notification.Local.Reminder( + sessionId = Session.Id(sessionId), + ) + } + + private fun Intent.parseFeedbackNotification(): Notification.Local.Feedback? { + val sessionId = this.getStringExtra(Notification.Keys.SESSION_ID) ?: run { + log.e { "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." } + return null + } + + return Notification.Local.Feedback( + sessionId = Session.Id(sessionId), + ) + } + private fun createPendingIntent(id: Int, intentTransform: Intent.() -> Unit = {}): PendingIntent { val intent = IdentifiableIntent("$id", context, NotificationPublisher::class.java).apply(intentTransform) return PendingIntent.getBroadcast( @@ -188,14 +256,6 @@ class AndroidNotificationService( settings[NOTIFICATION_ID_MAP_KEY] = json.encodeToString(registeredNotifications) } - /* - private data class NotificationIds( - var reminderId: Int?, - var dismissId: Int?, - var feedbackId: Int?, - ) - */ - companion object { private const val NOTIFICATION_CHANNEL_ID = "NOTIFICATION_CHANNEL_ID" diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationRescheduler.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationRescheduler.kt index 593cb0d5b..ab6cf9f0a 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationRescheduler.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationRescheduler.kt @@ -9,7 +9,9 @@ import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class NotificationRescheduler : BroadcastReceiver(), KoinComponent { +class NotificationRescheduler : + BroadcastReceiver(), + KoinComponent { private val notificationSchedulingService by inject() override fun onReceive(context: Context?, intent: Intent?) { diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/AssetResourceReader.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/AssetResourceReader.kt index 49d0dae39..cd4ed6012 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/AssetResourceReader.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/AssetResourceReader.kt @@ -4,9 +4,7 @@ import android.content.Context import co.touchlab.droidcon.domain.service.impl.ResourceReader import java.io.InputStreamReader -class AssetResourceReader( - private val context: Context -) : ResourceReader { +class AssetResourceReader(private val context: Context) : ResourceReader { override fun readResource(name: String): String { // TODO: Catch Android-only exceptions and map them to common ones. return context.assets.open(name).use { stream -> diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/IdentifiableIntent.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/IdentifiableIntent.kt index 57dd958a1..c730b6b0a 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/IdentifiableIntent.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/IdentifiableIntent.kt @@ -3,11 +3,7 @@ package co.touchlab.droidcon.util import android.content.Context import android.content.Intent -class IdentifiableIntent( - private val id: String, - packageContext: Context, - cls: Class<*>, -) : Intent(packageContext, cls) { +class IdentifiableIntent(private val id: String, packageContext: Context, cls: Class<*>) : Intent(packageContext, cls) { override fun filterEquals(other: Intent?): Boolean { if (this === other) return true diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt index 2c095524f..e01a7551c 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt @@ -2,13 +2,13 @@ package co.touchlab.droidcon.util.formatter import co.touchlab.droidcon.Constants.conferenceTimeZone import co.touchlab.droidcon.domain.service.DateTimeService -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.atTime import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.atTime class AndroidDateFormatter(private val dateTimeService: DateTimeService) : DateFormatter { @@ -18,19 +18,14 @@ class AndroidDateFormatter(private val dateTimeService: DateTimeService) : DateF private val minuteHourTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) .apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } - override fun monthWithDay(date: LocalDate): String { - return shortDateFormat.format( - Date(with(dateTimeService) { date.atTime(0, 0).fromConferenceDateTime() }.toEpochMilliseconds()) - ).uppercase() - } + override fun monthWithDay(date: LocalDate): String = shortDateFormat.format( + Date(with(dateTimeService) { date.atTime(0, 0).fromConferenceDateTime() }.toEpochMilliseconds()), + ).uppercase() - override fun timeOnly(dateTime: LocalDateTime): String? { - return minuteHourTimeFormat.format( - Date(with(dateTimeService) { dateTime.fromConferenceDateTime() }.toEpochMilliseconds()) - ) - } + override fun timeOnly(dateTime: LocalDateTime): String? = minuteHourTimeFormat.format( + Date(with(dateTimeService) { dateTime.fromConferenceDateTime() }.toEpochMilliseconds()), + ) - override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime): String { - return timeOnly(fromDateTime) + " - " + timeOnly(toDateTime) - } + override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime): String = + timeOnly(fromDateTime) + " - " + timeOnly(toDateTime) } diff --git a/shared/src/androidMain/res/drawable/ic_baseline_insert_invitation_24.xml b/shared/src/androidMain/res/drawable/ic_baseline_insert_invitation_24.xml index 98cf2e2a9..9087f64a5 100755 --- a/shared/src/androidMain/res/drawable/ic_baseline_insert_invitation_24.xml +++ b/shared/src/androidMain/res/drawable/ic_baseline_insert_invitation_24.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,12h-5v5h5v-5zM16,1v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2h-1L18,1h-2zM19,19L5,19L5,8h14v11z" /> diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/Constants.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/Constants.kt index 14348d337..345a4e623 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/Constants.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/Constants.kt @@ -2,6 +2,7 @@ package co.touchlab.droidcon import kotlinx.datetime.TimeZone +@Suppress("ktlint:standard:property-naming") object Constants { val conferenceTimeZone = TimeZone.of("Europe/London") @@ -9,7 +10,8 @@ object Constants { * String of `${timeZoneString}|${Sessionize.scheduleId}` run through md5 * When time zone is changed, generate a new hash by running `md5 -s "${timeZoneString}|${Sessionize.scheduleId}"` in command line */ - val conferenceTimeZoneHash = "13a8a14c5b52a276b2fd9491ed180697" + val conferenceTimeZoneHash = "7f5ba3fbad43896bad6847f6e8c662ac" + val showVenueMap: Boolean = true object Firestore { @@ -17,13 +19,21 @@ object Constants { const val databaseName = "(default)" // Known variants: "sponsors", "sponsors-lisbon-2019", "sponsors-sf-2019", "sponsors-sf-2022", "sponsors-berlin-2022", "sponsors-nyc-2022" - const val collectionName = "sponsors-london-2023" + const val collectionName = "sponsors-london-2024" const val apiKey = "AIzaSyCkD5DH2rUJ8aZuJzANpIFj0AVuCNik1l0" } object Sessionize { - const val scheduleId = "64k7lmps" - const val sponsorsId = "64k7lmps" + const val scheduleId = "78xrdv22" + const val sponsorsId = "78xrdv22" + } + + object SisterApp { + val showLaunchButton: Boolean = false + const val name = "Fluttercon" + const val androidPackageName = "co.touchlab.fluttercon" + const val iosUrlString = "fluttercon://open" + const val iosAppStoreUrlString = "https://apps.apple.com/app/fluttercon/id6670623431" } } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/Koin.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/Koin.kt index eda6e3b09..18f932f07 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/Koin.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/Koin.kt @@ -59,7 +59,7 @@ fun initKoin(additionalModules: List): KoinApplication { modules( additionalModules + platformModule + - coreModule + coreModule, ) } @@ -67,13 +67,9 @@ fun initKoin(additionalModules: List): KoinApplication { } val intToLongAdapter = object : ColumnAdapter { - override fun decode(databaseValue: Long): Int { - return databaseValue.toInt() - } + override fun decode(databaseValue: Long): Int = databaseValue.toInt() - override fun encode(value: Int): Long { - return value.toLong() - } + override fun encode(value: Int): Long = value.toLong() } private val coreModule = module { single { @@ -82,11 +78,11 @@ private val coreModule = module { sessionTableAdapter = SessionTable.Adapter( startsAtAdapter = InstantSqlDelightAdapter, endsAtAdapter = InstantSqlDelightAdapter, - feedbackRatingAdapter = intToLongAdapter + feedbackRatingAdapter = intToLongAdapter, ), sponsorGroupTableAdapter = SponsorGroupTable.Adapter( - intToLongAdapter - ) + intToLongAdapter, + ), ) } single { Clock.System } @@ -124,7 +120,7 @@ private val coreModule = module { single { SqlDelightSessionRepository( dateTimeService = get(), - sessionQueries = get().sessionQueries + sessionQueries = get().sessionQueries, ) } single { @@ -185,12 +181,12 @@ private val coreModule = module { } single { DefaultSettingsGateway( - settingsRepository = get() + settingsRepository = get(), ) } single { DefaultSettingsRepository( - observableSettings = get() + observableSettings = get(), ) } single { @@ -237,8 +233,6 @@ private val coreModule = module { } } -internal inline fun Scope.getWith(vararg params: Any?): T { - return get(parameters = { parametersOf(*params) }) -} +internal inline fun Scope.getWith(vararg params: Any?): T = get(parameters = { parametersOf(*params) }) expect val platformModule: Module diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/AboutItem.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/AboutItem.kt index 410d404ba..0b4631ff3 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/AboutItem.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/AboutItem.kt @@ -1,7 +1,3 @@ package co.touchlab.droidcon.application.composite -data class AboutItem( - val icon: String, - val title: String, - val detail: String, -) +data class AboutItem(val icon: String, val title: String, val detail: String) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/Settings.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/Settings.kt index ad851db9d..4d9bdd7b1 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/Settings.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/composite/Settings.kt @@ -1,7 +1,3 @@ package co.touchlab.droidcon.application.composite -data class Settings( - val isFeedbackEnabled: Boolean, - val isRemindersEnabled: Boolean, - val useComposeForIos: Boolean, -) +data class Settings(val isFeedbackEnabled: Boolean, val isRemindersEnabled: Boolean, val useComposeForIos: Boolean) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/gateway/impl/DefaultSettingsGateway.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/gateway/impl/DefaultSettingsGateway.kt index 80e860ec6..2bb3cca7c 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/gateway/impl/DefaultSettingsGateway.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/gateway/impl/DefaultSettingsGateway.kt @@ -5,9 +5,7 @@ import co.touchlab.droidcon.application.gateway.SettingsGateway import co.touchlab.droidcon.application.repository.SettingsRepository import kotlinx.coroutines.flow.StateFlow -class DefaultSettingsGateway( - private val settingsRepository: SettingsRepository, -) : SettingsGateway { +class DefaultSettingsGateway(private val settingsRepository: SettingsRepository) : SettingsGateway { override fun settings(): StateFlow = settingsRepository.settings diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultAboutRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultAboutRepository.kt index 235570a8a..4d85f948d 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultAboutRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultAboutRepository.kt @@ -4,17 +4,13 @@ import co.touchlab.droidcon.application.composite.AboutItem import co.touchlab.droidcon.application.repository.AboutRepository import co.touchlab.droidcon.domain.service.impl.json.AboutJsonResourceDataSource -class DefaultAboutRepository( - private val aboutJsonResourceDataSource: AboutJsonResourceDataSource, -) : AboutRepository { +class DefaultAboutRepository(private val aboutJsonResourceDataSource: AboutJsonResourceDataSource) : AboutRepository { - override suspend fun getAboutItems(): List { - return aboutJsonResourceDataSource.getAboutItems().map { - AboutItem( - icon = it.icon, - title = it.title, - detail = it.detail, - ) - } + override suspend fun getAboutItems(): List = aboutJsonResourceDataSource.getAboutItems().map { + AboutItem( + icon = it.icon, + title = it.title, + detail = it.detail, + ) } } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultSettingsRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultSettingsRepository.kt index 665632975..fae75afc7 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultSettingsRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/repository/impl/DefaultSettingsRepository.kt @@ -9,9 +9,7 @@ import com.russhwolf.settings.set import kotlinx.coroutines.flow.MutableStateFlow @OptIn(ExperimentalSettingsApi::class) -class DefaultSettingsRepository( - private val observableSettings: ObservableSettings, -) : SettingsRepository { +class DefaultSettingsRepository(private val observableSettings: ObservableSettings) : SettingsRepository { private companion object { private const val SETTINGS_FEEDBACK_ENABLED_KEY = "SETTINGS_FEEDBACK_ENABLED" private const val SETTINGS_REMINDERS_ENABLED_KEY = "SETTINGS_REMINDERS_ENABLED" @@ -41,7 +39,7 @@ class DefaultSettingsRepository( isFeedbackEnabled = isFeedbackEnabled, isRemindersEnabled = isRemindersEnabled, useComposeForIos = useComposeForIos, - ) + ), ) override suspend fun set(settings: Settings) { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/Notification.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/Notification.kt new file mode 100644 index 000000000..92e463a5e --- /dev/null +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/Notification.kt @@ -0,0 +1,32 @@ +package co.touchlab.droidcon.application.service + +import co.touchlab.droidcon.domain.entity.Session + +sealed interface Notification { + sealed interface DeepLink : Notification + + sealed interface Local : Notification { + data class Reminder(val sessionId: Session.Id) : + Local, + DeepLink + + data class Feedback(val sessionId: Session.Id) : + Local, + DeepLink + } + + sealed interface Remote : Notification { + data object RefreshData : Remote + } + + object Keys { + const val NOTIFICATION_TYPE = "notification_type" + const val SESSION_ID = "session_id" + } + + object Values { + const val REMINDER_TYPE = "reminder" + const val FEEDBACK_TYPE = "feedback" + const val REFRESH_DATA_TYPE = "refresh_data" + } +} diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt index aa88e9a47..6c9a1f543 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt @@ -1,19 +1,15 @@ package co.touchlab.droidcon.application.service import co.touchlab.droidcon.domain.entity.Session -import co.touchlab.droidcon.service.NotificationHandler +import co.touchlab.droidcon.service.DeepLinkNotificationHandler import kotlinx.datetime.Instant interface NotificationService { suspend fun initialize(): Boolean - suspend fun schedule(type: NotificationType, sessionId: Session.Id, title: String, body: String, delivery: Instant, dismiss: Instant?) + suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) suspend fun cancel(sessionIds: List) - fun setHandler(notificationHandler: NotificationHandler) - - enum class NotificationType { - Reminder, Feedback - } + fun setHandler(notificationHandler: DeepLinkNotificationHandler) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt index 5c289c39b..e62d2d206 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt @@ -2,13 +2,13 @@ package co.touchlab.droidcon.application.service.impl import co.touchlab.droidcon.application.composite.Settings import co.touchlab.droidcon.application.repository.SettingsRepository +import co.touchlab.droidcon.application.service.Notification import co.touchlab.droidcon.application.service.NotificationSchedulingService import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.entity.Session import co.touchlab.droidcon.domain.repository.RoomRepository import co.touchlab.droidcon.domain.repository.SessionRepository import co.touchlab.droidcon.domain.service.DateTimeService -import com.russhwolf.settings.ExperimentalSettingsApi import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.set import kotlinx.coroutines.coroutineScope @@ -22,7 +22,6 @@ import kotlinx.datetime.plus import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -@OptIn(ExperimentalSettingsApi::class) class DefaultNotificationSchedulingService( private val sessionRepository: SessionRepository, private val roomRepository: RoomRepository, @@ -70,7 +69,7 @@ class DefaultNotificationSchedulingService( sessionFlow .combine( settingsFlow, - transform = { agenda, settings -> Triple(agenda, settings.isRemindersEnabled, settings.isFeedbackEnabled) } + transform = { agenda, settings -> Triple(agenda, settings.isRemindersEnabled, settings.isFeedbackEnabled) }, ) .distinctUntilChanged() .collect { (agenda, isRemindersEnabled, isFeedbackEnabled) -> @@ -93,14 +92,15 @@ class DefaultNotificationSchedulingService( session.startsAt.plus(NotificationSchedulingService.REMINDER_DELIVERY_START_OFFSET, DateTimeUnit.MINUTE) if (session.endsAt >= dateTimeService.now()) { notificationService.schedule( - type = NotificationService.NotificationType.Reminder, - sessionId = session.id, + notification = Notification.Local.Reminder( + sessionId = session.id, + ), title = localizedStringFactory.reminderTitle(roomName), body = localizedStringFactory.reminderBody(session.title), delivery = reminderDelivery, dismiss = reminderDelivery.plus( NotificationSchedulingService.REMINDER_DISMISS_OFFSET, - DateTimeUnit.MINUTE + DateTimeUnit.MINUTE, ), ) } @@ -111,8 +111,9 @@ class DefaultNotificationSchedulingService( session.endsAt.plus(NotificationSchedulingService.FEEDBACK_DISMISS_END_OFFSET, DateTimeUnit.MINUTE) if (feedbackDelivery.plus(24, DateTimeUnit.HOUR) >= dateTimeService.now() && session.feedback == null) { notificationService.schedule( - type = NotificationService.NotificationType.Feedback, - sessionId = session.id, + notification = Notification.Local.Feedback( + sessionId = session.id, + ), title = localizedStringFactory.feedbackTitle(), body = localizedStringFactory.feedbackBody(), delivery = feedbackDelivery, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/ScheduleItem.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/ScheduleItem.kt index dfc8ee8a1..8fce0afa1 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/ScheduleItem.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/ScheduleItem.kt @@ -4,9 +4,4 @@ import co.touchlab.droidcon.domain.entity.Profile import co.touchlab.droidcon.domain.entity.Room import co.touchlab.droidcon.domain.entity.Session -data class ScheduleItem( - val session: Session, - val isInConflict: Boolean, - val room: Room?, - val speakers: List, -) +data class ScheduleItem(val session: Session, val isInConflict: Boolean, val room: Room?, val speakers: List) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/SponsorGroupWithSponsors.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/SponsorGroupWithSponsors.kt index 51ef710ee..0189f8657 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/SponsorGroupWithSponsors.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/composite/SponsorGroupWithSponsors.kt @@ -3,7 +3,4 @@ package co.touchlab.droidcon.domain.composite import co.touchlab.droidcon.domain.entity.Sponsor import co.touchlab.droidcon.domain.entity.SponsorGroup -data class SponsorGroupWithSponsors( - val group: SponsorGroup, - val sponsors: List, -) +data class SponsorGroupWithSponsors(val group: SponsorGroup, val sponsors: List) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Room.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Room.kt index d3bfa39b7..7716393b1 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Room.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Room.kt @@ -1,8 +1,5 @@ package co.touchlab.droidcon.domain.entity -class Room( - override val id: Id, - val name: String, -) : DomainEntity() { +class Room(override val id: Id, val name: String) : DomainEntity() { data class Id(val value: Long) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Session.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Session.kt index 3a58a6c21..726812a05 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Session.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Session.kt @@ -21,16 +21,9 @@ class Session( data class Id(val value: String) - data class RSVP( - val isAttending: Boolean, - val isSent: Boolean, - ) + data class RSVP(val isAttending: Boolean, val isSent: Boolean) - data class Feedback( - val rating: Int, - val comment: String, - val isSent: Boolean, - ) { + data class Feedback(val rating: Int, val comment: String, val isSent: Boolean) { object Rating { const val DISSATISFIED = 1 const val NORMAL = 2 diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Sponsor.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Sponsor.kt index 93ec0485f..1bf0d84f8 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Sponsor.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/Sponsor.kt @@ -2,13 +2,8 @@ package co.touchlab.droidcon.domain.entity import co.touchlab.droidcon.composite.Url -class Sponsor( - override val id: Id, - val hasDetail: Boolean, - val description: String?, - val icon: Url, - val url: Url, -) : DomainEntity() { +class Sponsor(override val id: Id, val hasDetail: Boolean, val description: String?, val icon: Url, val url: Url) : + DomainEntity() { val name: String get() = id.name diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/SponsorGroup.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/SponsorGroup.kt index cfc107935..6f7991680 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/SponsorGroup.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/entity/SponsorGroup.kt @@ -1,10 +1,6 @@ package co.touchlab.droidcon.domain.entity -class SponsorGroup( - override val id: Id, - val displayPriority: Int, - val isProminent: Boolean, -) : DomainEntity() { +class SponsorGroup(override val id: Id, val displayPriority: Int, val isProminent: Boolean) : DomainEntity() { val name: String get() = id.value diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSessionGateway.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSessionGateway.kt index 382db92c2..32f0b2adf 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSessionGateway.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSessionGateway.kt @@ -17,35 +17,28 @@ class DefaultSessionGateway( private val scheduleService: ScheduleService, ) : SessionGateway { - override fun observeSchedule(): Flow> { - return sessionRepository.observeAll().map { sessions -> - sessions.map { session -> - scheduleItemForSession(session) - } + override fun observeSchedule(): Flow> = sessionRepository.observeAll().map { sessions -> + sessions.map { session -> + scheduleItemForSession(session) } } - override fun observeAgenda(): Flow> { - return sessionRepository.observeAllAttending().map { sessions -> - sessions.map { session -> - scheduleItemForSession(session) - } + override fun observeAgenda(): Flow> = sessionRepository.observeAllAttending().map { sessions -> + sessions.map { session -> + scheduleItemForSession(session) } } - override fun observeScheduleItem(id: Session.Id): Flow { - return sessionRepository.observe(id).map { session -> - scheduleItemForSession(session) - } + override fun observeScheduleItem(id: Session.Id): Flow = sessionRepository.observe(id).map { session -> + scheduleItemForSession(session) } - private suspend fun scheduleItemForSession(session: Session): ScheduleItem = - ScheduleItem( - session, - scheduleService.isInConflict(session), - session.room?.let { roomRepository.find(it) }, - profileRepository.getSpeakersBySession(session.id), - ) + private suspend fun scheduleItemForSession(session: Session): ScheduleItem = ScheduleItem( + session, + scheduleService.isInConflict(session), + session.room?.let { roomRepository.find(it) }, + profileRepository.getSpeakersBySession(session.id), + ) override suspend fun setAttending(session: Session, attending: Boolean) { sessionRepository.setRsvp(session.id, Session.RSVP(attending, false)) @@ -55,7 +48,5 @@ class DefaultSessionGateway( sessionRepository.setFeedback(session.id, feedback) } - override suspend fun getScheduleItem(id: Session.Id): ScheduleItem? { - return sessionRepository.find(id)?.let { scheduleItemForSession(it) } - } + override suspend fun getScheduleItem(id: Session.Id): ScheduleItem? = sessionRepository.find(id)?.let { scheduleItemForSession(it) } } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSponsorGateway.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSponsorGateway.kt index 89d63a5e3..6aeeb2814 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSponsorGateway.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/gateway/impl/DefaultSponsorGateway.kt @@ -16,19 +16,16 @@ class DefaultSponsorGateway( private val profileRepository: ProfileRepository, ) : SponsorGateway { - override fun observeSponsors(): Flow> = - sponsorGroupRepository.observeAll().map { groups -> - groups.map { group -> - SponsorGroupWithSponsors( - group, - sponsorRepository.allByGroupName(group.name) - ) - } + override fun observeSponsors(): Flow> = sponsorGroupRepository.observeAll().map { groups -> + groups.map { group -> + SponsorGroupWithSponsors( + group, + sponsorRepository.allByGroupName(group.name), + ) } + } - override fun observeSponsorById(id: Sponsor.Id): Flow = - sponsorRepository.observe(id) + override fun observeSponsorById(id: Sponsor.Id): Flow = sponsorRepository.observe(id) - override suspend fun getRepresentatives(sponsorId: Sponsor.Id): List = - profileRepository.getSponsorRepresentatives(sponsorId) + override suspend fun getRepresentatives(sponsorId: Sponsor.Id): List = profileRepository.getSponsorRepresentatives(sponsorId) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/BaseRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/BaseRepository.kt index 2790bd415..4c76ae719 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/BaseRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/BaseRepository.kt @@ -6,21 +6,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first abstract class BaseRepository> : Repository { - override suspend fun get(id: ID): ENTITY { - return observe(id).first() - } + override suspend fun get(id: ID): ENTITY = observe(id).first() - override suspend fun find(id: ID): ENTITY? { - return observeOrNull(id).first() - } + override suspend fun find(id: ID): ENTITY? = observeOrNull(id).first() - override fun observe(entity: ENTITY): Flow { - return observe(entity.id) - } + override fun observe(entity: ENTITY): Flow = observe(entity.id) - override suspend fun all(): List { - return observeAll().first() - } + override suspend fun all(): List = observeAll().first() override fun add(entity: ENTITY) { if (!contains(entity.id)) { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightProfileRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightProfileRepository.kt index 8a41a7fab..3077042a4 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightProfileRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightProfileRepository.kt @@ -19,11 +19,11 @@ class SqlDelightProfileRepository( private val profileQueries: ProfileQueries, private val speakerQueries: SessionSpeakerQueries, private val representativeQueries: SponsorRepresentativeQueries, -) : BaseRepository(), ProfileRepository { +) : BaseRepository(), + ProfileRepository { - override suspend fun getSpeakersBySession(id: Session.Id): List { - return profileQueries.selectBySession(id.value, ::profileFactory).executeAsList() - } + override suspend fun getSpeakersBySession(id: Session.Id): List = + profileQueries.selectBySession(id.value, ::profileFactory).executeAsList() override fun setSessionSpeakers(session: Session, speakers: List) { speakerQueries.deleteBySessionId(session.id.value) @@ -48,26 +48,19 @@ class SqlDelightProfileRepository( } } - override suspend fun getSponsorRepresentatives(sponsorId: Sponsor.Id): List { - return profileQueries.selectBySponsor(sponsorName = sponsorId.name, sponsorGroupName = sponsorId.group, mapper = ::profileFactory) + override suspend fun getSponsorRepresentatives(sponsorId: Sponsor.Id): List = + profileQueries.selectBySponsor(sponsorName = sponsorId.name, sponsorGroupName = sponsorId.group, mapper = ::profileFactory) .executeAsList() - } - override fun allSync(): List { - return profileQueries.selectAll(mapper = ::profileFactory).executeAsList() - } + override fun allSync(): List = profileQueries.selectAll(mapper = ::profileFactory).executeAsList() - override fun observe(id: Profile.Id): Flow { - return profileQueries.selectById(id.value, ::profileFactory).asFlow().mapToOne(Dispatchers.Main) - } + override fun observe(id: Profile.Id): Flow = + profileQueries.selectById(id.value, ::profileFactory).asFlow().mapToOne(Dispatchers.Main) - override fun observeOrNull(id: Profile.Id): Flow { - return profileQueries.selectById(id.value, ::profileFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - } + override fun observeOrNull(id: Profile.Id): Flow = + profileQueries.selectById(id.value, ::profileFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - override fun observeAll(): Flow> { - return profileQueries.selectAll(::profileFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAll(): Flow> = profileQueries.selectAll(::profileFactory).asFlow().mapToList(Dispatchers.Main) override fun doUpsert(entity: Profile) { profileQueries.upsert( @@ -86,9 +79,7 @@ class SqlDelightProfileRepository( profileQueries.delete(id.value) } - override fun contains(id: Profile.Id): Boolean { - return profileQueries.existsById(id.value).executeAsOne().toBoolean() - } + override fun contains(id: Profile.Id): Boolean = profileQueries.existsById(id.value).executeAsOne().toBoolean() private fun profileFactory( id: String, @@ -98,7 +89,7 @@ class SqlDelightProfileRepository( profilePicture: String?, twitter: String?, linkedIn: String?, - website: String? + website: String?, ) = Profile( id = Profile.Id(id), fullName = fullName, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightRoomRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightRoomRepository.kt index 2c581a2a4..271ef2647 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightRoomRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightRoomRepository.kt @@ -10,23 +10,18 @@ import co.touchlab.droidcon.domain.repository.RoomRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -class SqlDelightRoomRepository( - private val roomQueries: RoomQueries, -) : BaseRepository(), RoomRepository { +class SqlDelightRoomRepository(private val roomQueries: RoomQueries) : + BaseRepository(), + RoomRepository { override fun allSync(): List = roomQueries.selectAll(::roomFactory).executeAsList() - override fun observe(id: Room.Id): Flow { - return roomQueries.selectById(id.value, ::roomFactory).asFlow().mapToOne(Dispatchers.Main) - } + override fun observe(id: Room.Id): Flow = roomQueries.selectById(id.value, ::roomFactory).asFlow().mapToOne(Dispatchers.Main) - override fun observeOrNull(id: Room.Id): Flow { - return roomQueries.selectById(id.value, ::roomFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - } + override fun observeOrNull(id: Room.Id): Flow = + roomQueries.selectById(id.value, ::roomFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - override fun observeAll(): Flow> { - return roomQueries.selectAll(::roomFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAll(): Flow> = roomQueries.selectAll(::roomFactory).asFlow().mapToList(Dispatchers.Main) override fun doUpsert(entity: Room) { roomQueries.upsert(id = entity.id.value, name = entity.name) @@ -36,9 +31,7 @@ class SqlDelightRoomRepository( roomQueries.deleteById(id.value) } - override fun contains(id: Room.Id): Boolean { - return roomQueries.existsById(id.value).executeAsOne() != 0L - } + override fun contains(id: Room.Id): Boolean = roomQueries.existsById(id.value).executeAsOne() != 0L private fun roomFactory(id: Long, name: String) = Room(id = Room.Id(id), name = name) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt index 42a258885..24d5857b0 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt @@ -14,27 +14,21 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.datetime.Instant -class SqlDelightSessionRepository( - private val dateTimeService: DateTimeService, - private val sessionQueries: SessionQueries, -) : BaseRepository(), SessionRepository { - override fun observe(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOne(Dispatchers.Main) - } +class SqlDelightSessionRepository(private val dateTimeService: DateTimeService, private val sessionQueries: SessionQueries) : + BaseRepository(), + SessionRepository { + override fun observe(id: Session.Id): Flow = + sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOne(Dispatchers.Main) fun sessionById(id: Session.Id): Session? = sessionQueries.sessionById(id.value, ::sessionFactory).executeAsOneOrNull() - override fun observeOrNull(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - } + override fun observeOrNull(id: Session.Id): Flow = + sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - override fun observeAllAttending(): Flow> { - return sessionQueries.attendingSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAllAttending(): Flow> = + sessionQueries.attendingSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) - override suspend fun allAttending(): List { - return observeAllAttending().first() - } + override suspend fun allAttending(): List = observeAllAttending().first() override suspend fun setRsvp(sessionId: Session.Id, rsvp: Session.RSVP) { sessionQueries.updateRsvp(rsvp.isAttending.toLong(), sessionId.value) @@ -56,9 +50,7 @@ class SqlDelightSessionRepository( override fun findSync(id: Session.Id): Session? = sessionQueries.sessionById(id.value, mapper = ::sessionFactory).executeAsOneOrNull() - override fun observeAll(): Flow> { - return sessionQueries.allSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAll(): Flow> = sessionQueries.allSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) override fun doUpsert(entity: Session) { sessionQueries.upsert( @@ -73,17 +65,13 @@ class SqlDelightSessionRepository( rsvpSent = entity.rsvp.isSent.toLong(), feedbackRating = entity.feedback?.rating, feedbackComment = entity.feedback?.comment, - feedbackSent = entity.feedback?.isSent?.toLong() ?: 0 + feedbackSent = entity.feedback?.isSent?.toLong() ?: 0, ) } - override fun doDelete(id: Session.Id) { - return sessionQueries.deleteById(id.value) - } + override fun doDelete(id: Session.Id) = sessionQueries.deleteById(id.value) - override fun contains(id: Session.Id): Boolean { - return sessionQueries.existsById(id.value).executeAsOne().toBoolean() - } + override fun contains(id: Session.Id): Boolean = sessionQueries.existsById(id.value).executeAsOne().toBoolean() private fun sessionFactory( id: String, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt index 6b3108600..145bb588b 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt @@ -10,27 +10,22 @@ import co.touchlab.droidcon.domain.repository.SponsorGroupRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -class SqlDelightSponsorGroupRepository( - private val sponsorGroupQueries: SponsorGroupQueries, -) : BaseRepository(), SponsorGroupRepository { +class SqlDelightSponsorGroupRepository(private val sponsorGroupQueries: SponsorGroupQueries) : + BaseRepository(), + SponsorGroupRepository { override fun allSync(): List = sponsorGroupQueries.selectAll(::sponsorGroupFactory).executeAsList() - override fun observe(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOne(Dispatchers.Main) - } + override fun observe(id: SponsorGroup.Id): Flow = + sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOne(Dispatchers.Main) - override fun observeOrNull(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - } + override fun observeOrNull(id: SponsorGroup.Id): Flow = + sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - override fun observeAll(): Flow> { - return sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAll(): Flow> = + sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow().mapToList(Dispatchers.Main) - override fun contains(id: SponsorGroup.Id): Boolean { - return sponsorGroupQueries.existsByName(id.value).executeAsOne().toBoolean() - } + override fun contains(id: SponsorGroup.Id): Boolean = sponsorGroupQueries.existsByName(id.value).executeAsOne().toBoolean() override fun doUpsert(entity: SponsorGroup) { sponsorGroupQueries.upsert( @@ -44,11 +39,7 @@ class SqlDelightSponsorGroupRepository( sponsorGroupQueries.deleteByName(id.value) } - private fun sponsorGroupFactory( - name: String, - displayPriority: Int, - isProminent: Boolean, - ) = SponsorGroup( + private fun sponsorGroupFactory(name: String, displayPriority: Int, isProminent: Boolean) = SponsorGroup( id = SponsorGroup.Id(name), displayPriority = displayPriority, isProminent = isProminent, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt index 5f7986979..9d95d8c1e 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt @@ -11,29 +11,22 @@ import co.touchlab.droidcon.domain.repository.SponsorRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -class SqlDelightSponsorRepository( - private val sponsorQueries: SponsorQueries, -) : BaseRepository(), SponsorRepository { +class SqlDelightSponsorRepository(private val sponsorQueries: SponsorQueries) : + BaseRepository(), + SponsorRepository { - override fun observe(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOne(Dispatchers.Main) - } + override fun observe(id: Sponsor.Id): Flow = + sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOne(Dispatchers.Main) - override fun observeOrNull(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - } + override fun observeOrNull(id: Sponsor.Id): Flow = + sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOneOrNull(Dispatchers.Main) - override fun observeAll(): Flow> { - return sponsorQueries.selectAll(::sponsorFactory).asFlow().mapToList(Dispatchers.Main) - } + override fun observeAll(): Flow> = sponsorQueries.selectAll(::sponsorFactory).asFlow().mapToList(Dispatchers.Main) - override fun contains(id: Sponsor.Id): Boolean { - return sponsorQueries.existsById(id.name, id.group).executeAsOne().toBoolean() - } + override fun contains(id: Sponsor.Id): Boolean = sponsorQueries.existsById(id.name, id.group).executeAsOne().toBoolean() - override suspend fun allByGroupName(group: String): List { - return sponsorQueries.sponsorsByGroup(group, ::sponsorFactory).executeAsList() - } + override suspend fun allByGroupName(group: String): List = + sponsorQueries.sponsorsByGroup(group, ::sponsorFactory).executeAsList() override fun allSync(): List = sponsorQueries.selectAll(::sponsorFactory).executeAsList() @@ -52,18 +45,12 @@ class SqlDelightSponsorRepository( sponsorQueries.deleteById(id.name, id.group) } - private fun sponsorFactory( - name: String, - groupName: String, - hasDetail: Boolean, - description: String?, - iconUrl: String, - url: String, - ) = Sponsor( - id = Sponsor.Id(name, groupName), - hasDetail = hasDetail, - description = description, - icon = Url(iconUrl), - url = Url(url), - ) + private fun sponsorFactory(name: String, groupName: String, hasDetail: Boolean, description: String?, iconUrl: String, url: String) = + Sponsor( + id = Sponsor.Id(name, groupName), + hasDetail = hasDetail, + description = description, + icon = Url(iconUrl), + url = Url(url), + ) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/SyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/SyncService.kt index 29f81d630..f751617ac 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/SyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/SyncService.kt @@ -3,4 +3,6 @@ package co.touchlab.droidcon.domain.service interface SyncService { suspend fun runSynchronization() + + suspend fun forceSynchronize(): Boolean } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultApiDataSource.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultApiDataSource.kt index 1197e3be6..20c269b1c 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultApiDataSource.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultApiDataSource.kt @@ -14,10 +14,7 @@ import io.ktor.http.takeFrom import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.Json -class DefaultApiDataSource( - private val client: HttpClient, - private val json: Json, -) : DefaultSyncService.DataSource { +class DefaultApiDataSource(private val client: HttpClient, private val json: Json) : DefaultSyncService.DataSource { override suspend fun getSpeakers(): List { val jsonString = client.get { // We want to use the `sponsorsId` to get "speakers" for the sponsors as well as speakers for real sessions. diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultDateTimeService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultDateTimeService.kt index 5575bd9e4..52e05fd21 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultDateTimeService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultDateTimeService.kt @@ -8,20 +8,13 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime -class DefaultDateTimeService( - private val clock: Clock, - private val conferenceTimeZone: TimeZone, -) : DateTimeService { +class DefaultDateTimeService(private val clock: Clock, private val conferenceTimeZone: TimeZone) : DateTimeService { override fun now(): Instant = clock.now() override fun conferenceNow(): LocalDateTime = now().toConferenceDateTime() - override fun Instant.toConferenceDateTime(): LocalDateTime { - return toLocalDateTime(conferenceTimeZone) - } + override fun Instant.toConferenceDateTime(): LocalDateTime = toLocalDateTime(conferenceTimeZone) - override fun LocalDateTime.fromConferenceDateTime(): Instant { - return toInstant(conferenceTimeZone) - } + override fun LocalDateTime.fromConferenceDateTime(): Instant = toInstant(conferenceTimeZone) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultFeedbackService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultFeedbackService.kt index 14d280bc4..e9e169a58 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultFeedbackService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultFeedbackService.kt @@ -26,11 +26,9 @@ class DefaultFeedbackService( json.decodeFromString(it) } ?: emptySet() - override suspend fun next(): Session? { - return sessionGateway.observeAgenda().first() - .firstOrNull { it.session.endsAt < clock.now() && !completedSessionIds.contains(it.session.id.value) } - ?.session - } + override suspend fun next(): Session? = sessionGateway.observeAgenda().first() + .firstOrNull { it.session.endsAt < clock.now() && !completedSessionIds.contains(it.session.id.value) } + ?.session override suspend fun submit(session: Session, feedback: Session.Feedback) { sessionGateway.setFeedback(session, feedback) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultScheduleService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultScheduleService.kt index 2e468e1f8..a98128399 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultScheduleService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultScheduleService.kt @@ -5,9 +5,7 @@ import co.touchlab.droidcon.domain.repository.SessionRepository import co.touchlab.droidcon.domain.service.ScheduleService import kotlinx.datetime.Instant -class DefaultScheduleService( - private val sessionRepository: SessionRepository, -) : ScheduleService { +class DefaultScheduleService(private val sessionRepository: SessionRepository) : ScheduleService { override suspend fun isInConflict(session: Session): Boolean { if (!session.rsvp.isAttending) { @@ -19,8 +17,7 @@ class DefaultScheduleService( } } - private fun ClosedRange.intersects(otherRange: ClosedRange): Boolean { - return start.epochSeconds < otherRange.endInclusive.epochSeconds && + private fun ClosedRange.intersects(otherRange: ClosedRange): Boolean = + start.epochSeconds < otherRange.endInclusive.epochSeconds && endInclusive.epochSeconds > otherRange.start.epochSeconds - } } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultServerApi.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultServerApi.kt index 3bbfa1d1e..116d4712c 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultServerApi.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultServerApi.kt @@ -13,11 +13,7 @@ import io.ktor.http.isSuccess import io.ktor.http.takeFrom import kotlinx.serialization.json.Json -class DefaultServerApi( - private val userIdProvider: UserIdProvider, - private val client: HttpClient, - private val json: Json, -) : ServerApi { +class DefaultServerApi(private val userIdProvider: UserIdProvider, private val client: HttpClient, private val json: Json) : ServerApi { override suspend fun setRsvp(sessionId: Session.Id, isAttending: Boolean): Boolean { val methodName = if (isAttending) { "sessionizeRsvpEvent" @@ -31,16 +27,14 @@ class DefaultServerApi( }.status.isSuccess() } - override suspend fun setFeedback(sessionId: Session.Id, rating: Int, comment: String): Boolean { - return client.submitForm( - formParameters = Parameters.build { - append("rating", rating.toString()) - append("comment", comment) - } - ) { - droidcon("/dataTest/sessionizeFeedbackEvent/${sessionId.value}/${userIdProvider.getId()}") - }.status.isSuccess() - } + override suspend fun setFeedback(sessionId: Session.Id, rating: Int, comment: String): Boolean = client.submitForm( + formParameters = Parameters.build { + append("rating", rating.toString()) + append("comment", comment) + }, + ) { + droidcon("/dataTest/sessionizeFeedbackEvent/${sessionId.value}/${userIdProvider.getId()}") + }.status.isSuccess() private fun HttpRequestBuilder.droidcon(path: String) { url { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt index 43d290c9c..2ecd5d1ea 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt @@ -22,7 +22,6 @@ import co.touchlab.droidcon.domain.service.impl.dto.SpeakersDto.LinkType import co.touchlab.droidcon.domain.service.impl.dto.SponsorSessionsDto import co.touchlab.droidcon.domain.service.impl.dto.SponsorsDto import co.touchlab.kermit.Logger -import com.russhwolf.settings.ExperimentalSettingsApi import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.get import com.russhwolf.settings.set @@ -35,7 +34,6 @@ import kotlinx.datetime.Instant import kotlinx.datetime.LocalDateTime import kotlinx.datetime.minus -@OptIn(ExperimentalSettingsApi::class) class DefaultSyncService( private val log: Logger, private val settings: ObservableSettings, @@ -62,8 +60,10 @@ class DefaultSyncService( private const val SESSIONIZE_SYNC_SINCE_LAST_MINUTES = 15 private const val SESSIONIZE_SYNC_NEXT_DELAY: Long = 1L * 60L * 60L * 1000L + // 5 minutes private const val RSVP_SYNC_DELAY: Long = 5L * 60L * 1000L + // 5 minutes private const val FEEDBACK_SYNC_DELAY: Long = 5L * 60L * 1000L } @@ -88,17 +88,18 @@ class DefaultSyncService( while (isActive) { val lastSessionizeSync = lastSessionizeSync // If this is the first Sessionize sync or if the last sync occurred more than 2 hours ago. - if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE)) { - log.d { "Will sync all repositories from API data source." } + if ( + lastSessionizeSync == null || + lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE) + ) { try { - updateRepositoriesFromDataSource(apiDataSource) + runApiDataSourcesSynchronization() } catch (e: Exception) { log.w(e) { "Failed to update repositories from API data source." } delay(SESSIONIZE_SYNC_POLL_DELAY) continue } log.d { "Sync successful, waiting for next sync in $SESSIONIZE_SYNC_NEXT_DELAY ms." } - this@DefaultSyncService.lastSessionizeSync = dateTimeService.now() delay(SESSIONIZE_SYNC_NEXT_DELAY) } else { log.d { "The sync didn't happen, so we'll try again in a short while ($SESSIONIZE_SYNC_POLL_DELAY ms)." } @@ -167,6 +168,14 @@ class DefaultSyncService( } } + override suspend fun forceSynchronize(): Boolean = try { + runApiDataSourcesSynchronization() + true + } catch (e: Exception) { + log.e(e) { "Failed to update repositories from API data source." } + false + } + private suspend fun seedLocalRepositoriesIfNeeded() { if (isLocalRepositoriesSeeded) { return @@ -177,6 +186,12 @@ class DefaultSyncService( isLocalRepositoriesSeeded = true } + private suspend fun runApiDataSourcesSynchronization() { + log.d { "Will sync all repositories from API data source." } + updateRepositoriesFromDataSource(apiDataSource) + lastSessionizeSync = dateTimeService.now() + } + private suspend fun updateRepositoriesFromDataSource(dataSource: DataSource) { val speakerDtos = dataSource.getSpeakers() val days = dataSource.getSchedule() @@ -201,7 +216,7 @@ class DefaultSyncService( // Remove deleted speakers. profileRepository.allSync().map { it.id } - .subtract(profiles.map { it.id }) + .subtract(profiles.map { it.id }.toSet()) .forEach { profileRepository.remove(it) } profiles.forEach { @@ -241,7 +256,7 @@ class DefaultSyncService( // Remove deleted rooms. roomRepository.allSync().map { it.id } - .subtract(rooms.map { it.id }) + .subtract(rooms.map { it.id }.toSet()) .forEach { roomRepository.remove(it) } rooms.forEach { room -> @@ -251,7 +266,7 @@ class DefaultSyncService( // Remove deleted sessions. sessionRepository.allSync() .map { it.id } - .subtract(sessionsAndSpeakers.map { it.first.id }) + .subtract(sessionsAndSpeakers.map { it.first.id }.toSet()) .forEach { sessionRepository.remove(it) } sessionsAndSpeakers.forEach { (updatedSession, speakers) -> @@ -268,7 +283,10 @@ class DefaultSyncService( } } - private fun updateSponsorsFromDataSource(sponsorSessionsGroups: List, sponsors: SponsorsDto.SponsorCollectionDto) { + private fun updateSponsorsFromDataSource( + sponsorSessionsGroups: List, + sponsors: SponsorsDto.SponsorCollectionDto, + ): String { val sponsorSessions = sponsorSessionsGroups.flatMap { it.sessions }.associateBy { it.id } val sponsorGroupsToSponsorDtos = sponsors.groups.map { group -> val groupName = (group.name.split('/').lastOrNull() ?: group.name) @@ -279,7 +297,7 @@ class DefaultSyncService( SponsorGroup( id = SponsorGroup.Id(groupName), displayPriority = group.fields.displayOrder.integerValue.toInt(), - isProminent = group.fields.prominent?.booleanValue ?: false + isProminent = group.fields.prominent?.booleanValue ?: false, ) to group.fields.sponsors.arrayValue.values.map { it.mapValue.fields } } @@ -299,11 +317,11 @@ class DefaultSyncService( } sponsorRepository.allSync().map { it.id } - .subtract(sponsorsAndRepresentativeIds.map { it.first.id }) + .subtract(sponsorsAndRepresentativeIds.map { it.first.id }.toSet()) .forEach { sponsorRepository.remove(it) } sponsorGroupRepository.allSync().map { it.id } - .subtract(sponsorGroupsToSponsorDtos.map { it.first.id }) + .subtract(sponsorGroupsToSponsorDtos.map { it.first.id }.toSet()) .forEach { sponsorGroupRepository.remove(it) } sponsorGroupsToSponsorDtos.forEach { (group, _) -> @@ -315,6 +333,7 @@ class DefaultSyncService( profileRepository.setSponsorRepresentatives(sponsor, representativeIds) } + return "" } private fun profileFactory(speakerDto: SpeakersDto.SpeakerDto): Profile { @@ -335,7 +354,8 @@ class DefaultSyncService( interface DataSource { enum class Kind { - Seed, Api + Seed, + Api, } suspend fun getSpeakers(): List diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultUserIdProvider.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultUserIdProvider.kt index 53959911e..570341a90 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultUserIdProvider.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultUserIdProvider.kt @@ -8,9 +8,7 @@ import com.russhwolf.settings.get import com.russhwolf.settings.set @OptIn(ExperimentalSettingsApi::class) -class DefaultUserIdProvider( - private val observableSettings: ObservableSettings, -) : UserIdProvider { +class DefaultUserIdProvider(private val observableSettings: ObservableSettings) : UserIdProvider { companion object { const val USER_ID_KEY = "USER_ID_KEY" } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/AboutDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/AboutDto.kt index 5326ab829..c69ae7b7e 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/AboutDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/AboutDto.kt @@ -5,9 +5,5 @@ import kotlinx.serialization.Serializable object AboutDto { @Serializable - data class AboutItemDto( - val icon: String, - val title: String, - val detail: String, - ) + data class AboutItemDto(val icon: String, val title: String, val detail: String) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/ScheduleDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/ScheduleDto.kt index ae4de1ff9..e3d264c10 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/ScheduleDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/ScheduleDto.kt @@ -7,17 +7,10 @@ import kotlinx.serialization.json.JsonArray object ScheduleDto { @Serializable - data class DayDto( - val date: String, - val rooms: List, - ) + data class DayDto(val date: String, val rooms: List) @Serializable - data class RoomDto( - val id: Long, - val name: String, - val sessions: List, - ) + data class RoomDto(val id: Long, val name: String, val sessions: List) @Serializable data class SessionDto( @@ -39,10 +32,7 @@ object ScheduleDto { ) @Serializable - data class SpeakerDto( - val id: String, - val name: String, - ) + data class SpeakerDto(val id: String, val name: String) fun List.sessions(): List = flatMap { day -> day.rooms.flatMap { room -> diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt index 3aa16801f..741eda486 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt @@ -28,11 +28,7 @@ object SpeakersDto { ) @Serializable - data class LinkDto( - val title: String, - val url: String, - val linkType: LinkType - ) + data class LinkDto(val title: String, val url: String, val linkType: LinkType) @Serializable(with = LinkType.Companion::class) data class LinkType(val value: String) { @@ -67,8 +63,5 @@ object SpeakersDto { } @Serializable - data class SessionDto( - val id: Long, - val name: String - ) + data class SessionDto(val id: Long, val name: String) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorSessionsDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorSessionsDto.kt index 3aa253fcf..06abfbc48 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorSessionsDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorSessionsDto.kt @@ -5,21 +5,11 @@ import kotlinx.serialization.Serializable object SponsorSessionsDto { @Serializable - data class SessionGroupDto( - val sessions: List - ) + data class SessionGroupDto(val sessions: List) @Serializable - data class SessionDto( - val id: String, - val title: String, - val description: String?, - val speakers: List, - ) + data class SessionDto(val id: String, val title: String, val description: String?, val speakers: List) @Serializable - data class SpeakerReferenceDto( - val id: String, - val name: String, - ) + data class SpeakerReferenceDto(val id: String, val name: String) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorsDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorsDto.kt index 40c4e136c..a48ba4885 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorsDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SponsorsDto.kt @@ -12,60 +12,32 @@ object SponsorsDto { ) @Serializable - data class SponsorGroupDto( - val name: String, - val fields: DocumentFields, - val createTime: String, - val updateTime: String, - ) + data class SponsorGroupDto(val name: String, val fields: DocumentFields, val createTime: String, val updateTime: String) @Serializable - data class DocumentFields( - val displayOrder: DisplayOrder, - val sponsors: Sponsors, - val prominent: BooleanValue? = null, - ) + data class DocumentFields(val displayOrder: DisplayOrder, val sponsors: Sponsors, val prominent: BooleanValue? = null) @Serializable - data class DisplayOrder( - val integerValue: String, - ) + data class DisplayOrder(val integerValue: String) @Serializable - data class Sponsors( - val arrayValue: ArrayValue, - ) + data class Sponsors(val arrayValue: ArrayValue) @Serializable - data class ArrayValue( - val values: List, - ) + data class ArrayValue(val values: List) @Serializable - data class Value( - val mapValue: MapValue, - ) + data class Value(val mapValue: MapValue) @Serializable - data class MapValue( - val fields: MapValueFields, - ) + data class MapValue(val fields: MapValueFields) @Serializable - data class MapValueFields( - val sponsorId: StringValue? = null, - val name: StringValue, - val icon: StringValue, - val url: StringValue, - ) + data class MapValueFields(val sponsorId: StringValue? = null, val name: StringValue, val icon: StringValue, val url: StringValue) @Serializable - data class StringValue( - val stringValue: String, - ) + data class StringValue(val stringValue: String) @Serializable - data class BooleanValue( - val booleanValue: Boolean, - ) + data class BooleanValue(val booleanValue: Boolean) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt index 7d9e7419b..140e090fa 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt @@ -5,7 +5,6 @@ import kotlinx.serialization.builtins.ListSerializer class AboutJsonResourceDataSource(private val jsonResourceReader: JsonResourceReader) { - fun getAboutItems(): List { - return jsonResourceReader.readAndDecodeResource("about.json", ListSerializer(AboutDto.AboutItemDto.serializer())) - } + fun getAboutItems(): List = + jsonResourceReader.readAndDecodeResource("about.json", ListSerializer(AboutDto.AboutItemDto.serializer())) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonResourceReader.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonResourceReader.kt index adba7ad66..dab005b62 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonResourceReader.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonResourceReader.kt @@ -4,10 +4,7 @@ import co.touchlab.droidcon.domain.service.impl.ResourceReader import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.json.Json -class JsonResourceReader( - private val resourceReader: ResourceReader, - private val json: Json, -) { +class JsonResourceReader(private val resourceReader: ResourceReader, private val json: Json) { internal fun readAndDecodeResource(name: String, strategy: DeserializationStrategy): T { val text = resourceReader.readResource(name) return json.decodeFromString(strategy, text) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonSeedResourceDataSource.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonSeedResourceDataSource.kt index fdb4c7143..9ceee8e30 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonSeedResourceDataSource.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/JsonSeedResourceDataSource.kt @@ -7,29 +7,21 @@ import co.touchlab.droidcon.domain.service.impl.dto.SponsorSessionsDto import co.touchlab.droidcon.domain.service.impl.dto.SponsorsDto import kotlinx.serialization.builtins.ListSerializer -class JsonSeedResourceDataSource( - private val jsonResourceReader: JsonResourceReader -) : DefaultSyncService.DataSource { +class JsonSeedResourceDataSource(private val jsonResourceReader: JsonResourceReader) : DefaultSyncService.DataSource { - override suspend fun getSpeakers(): List { - return jsonResourceReader.readAndDecodeResource("speakers.json", ListSerializer(SpeakersDto.SpeakerDto.serializer())) - } + override suspend fun getSpeakers(): List = + jsonResourceReader.readAndDecodeResource("speakers.json", ListSerializer(SpeakersDto.SpeakerDto.serializer())) - override suspend fun getSchedule(): List { - return jsonResourceReader.readAndDecodeResource("schedule.json", ListSerializer(ScheduleDto.DayDto.serializer())) - } + override suspend fun getSchedule(): List = + jsonResourceReader.readAndDecodeResource("schedule.json", ListSerializer(ScheduleDto.DayDto.serializer())) - override suspend fun getSponsorSessions(): List { - return jsonResourceReader.readAndDecodeResource( - "sponsor_sessions.json", - ListSerializer(SponsorSessionsDto.SessionGroupDto.serializer()) - ) - } + override suspend fun getSponsorSessions(): List = jsonResourceReader.readAndDecodeResource( + "sponsor_sessions.json", + ListSerializer(SponsorSessionsDto.SessionGroupDto.serializer()), + ) - override suspend fun getSponsors(): SponsorsDto.SponsorCollectionDto { - return jsonResourceReader.readAndDecodeResource( - "sponsors.json", - SponsorsDto.SponsorCollectionDto.serializer(), - ) - } + override suspend fun getSponsors(): SponsorsDto.SponsorCollectionDto = jsonResourceReader.readAndDecodeResource( + "sponsors.json", + SponsorsDto.SponsorCollectionDto.serializer(), + ) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/dto/WebLink.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/dto/WebLink.kt index 4e73642a6..08eaa1965 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/dto/WebLink.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/dto/WebLink.kt @@ -6,7 +6,6 @@ data class WebLink(val range: IntRange, val link: String) { companion object { - fun fromUrl(url: Url): WebLink = - WebLink(IntRange(0, url.string.length - 1), url.string) + fun fromUrl(url: Url): WebLink = WebLink(IntRange(0, url.string.length - 1), url.string) } } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/DeepLinkNotificationHandler.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/DeepLinkNotificationHandler.kt new file mode 100644 index 000000000..ddd703d56 --- /dev/null +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/DeepLinkNotificationHandler.kt @@ -0,0 +1,7 @@ +package co.touchlab.droidcon.service + +import co.touchlab.droidcon.application.service.Notification + +interface DeepLinkNotificationHandler { + fun handleDeepLinkNotification(notification: Notification.DeepLink) +} diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/NotificationHandler.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/NotificationHandler.kt deleted file mode 100644 index b4aa1bc51..000000000 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/service/NotificationHandler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package co.touchlab.droidcon.service - -import co.touchlab.droidcon.application.service.NotificationService - -interface NotificationHandler { - fun notificationReceived(sessionId: String, notificationType: NotificationService.NotificationType) -} diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/util/AppChecker.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/util/AppChecker.kt index 472a47671..a8e44aea4 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/util/AppChecker.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/util/AppChecker.kt @@ -15,7 +15,5 @@ object AppChecker { } } - private fun toMD5(text: String): String { - return text.encodeToByteArray().md5().hex - } + private fun toMD5(text: String): String = text.encodeToByteArray().md5().hex } diff --git a/shared/src/commonMain/resources/schedule.json b/shared/src/commonMain/resources/schedule.json index 64308c7b8..51e67758e 100644 --- a/shared/src/commonMain/resources/schedule.json +++ b/shared/src/commonMain/resources/schedule.json @@ -1,9589 +1,12251 @@ [ { - "date": "2023-10-26T00:00:00", + "date": "2024-10-31T00:00:00", "isDefault": true, "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "sessions": [ { - "id": "ca7e33b6-296c-4183-a100-266e11372aa3", + "id": "26c67130-cbaa-40e1-8270-4daea240daa2", "title": "Registration & Check-In", "description": null, - "startsAt": "2023-10-26T07:30:00", - "endsAt": "2023-10-26T09:00:00", + "startsAt": "2024-10-31T07:45:00", + "endsAt": "2024-10-31T09:00:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "1d7cbedc-b0c4-482c-8204-448935ab0f5c", + "id": "f1e96bdd-8d47-4dd2-b814-1421662f1da1", "title": "Welcome & Keynote", "description": null, - "startsAt": "2023-10-26T09:00:00", - "endsAt": "2023-10-26T09:15:00", + "startsAt": "2024-10-31T09:00:00", + "endsAt": "2024-10-31T09:10:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "529143", - "title": "Challenges, failures and lessons in 15 years of building on top of Android.", - "description": "15 years in Android development and a whole lot of fun—Carl , founder of Novoda and the guy (and friends) who kickstarted droidcon London, invites you to a whirlwind ride down memory lane. it's a celebration of Android, the community, and the crazy roller coaster of a journey they've been on together.\r\n\r\nGet ready for:\r\n\r\n- A trip back to Android's 'Wild West' days—remember when everyone was figuring it all out?\r\n- A toast to droidcon London, the event that became the go-to hub for Android aficionados.\r\n- Real talk on building Android teams—because, there has been a bit of learning ", - "startsAt": "2023-10-26T09:15:00", - "endsAt": "2023-10-26T10:05:00", + "id": "731273", + "title": "Project Sparkles: how Compose is changing Android Studio", + "description": "We use Android Studio every day, and appreciate how its rich feature set makes our job easier. Most people know that Android Studio is built on the IntelliJ Platform, the same that underpins the popular IntelliJ IDEA from JetBrains, which has seen lasting success for over 20 years. It’s a solid, expansive, and by far the best foundation on which we could stand on to deliver Android-oriented goodies.\r\n\r\nHowever, some parts of the IntelliJ Platform show the signs of time; in particular its UI framework, Swing, is proving the most limiting, having been around for almost 30 years. Don’t get us wrong — it works, and the IDEs themselves prove you can ship complex UIs by using Swing on the IntelliJ Platform. But as we looked at how nice it is to develop UIs on Android by using Jetpack Compose, we thought: why don’t we do the same?\r\n\r\nEnter Project Sparkles, which aims at gradually introducing new high-quality, polished UI surfaces in Android Studio, developed in Compose for Desktop, with all the bells and whistles you can expect from a top-tier interface. In this talk, we’ll cover how Project Sparkles is impacting the development of Android Studio, addressing long-standing user feedback, and how we’re working together with other teams at Google and JetBrains to build a framework to make your favourite IDE even better and easier to understand.\r\n\r\nWe’ll demonstrate a few examples of features already shipping that are powered by Project Sparkles, explain what our goals and ambitions are, and even show some sneak peeks of things you may see in a future Studio version. UI enthusiasts, assemble!", + "startsAt": "2024-10-31T09:10:00", + "endsAt": "2024-10-31T10:00:00", "isServiceSession": false, "isPlenumSession": true, "speakers": [ { - "id": "c74036f4-aebb-4fae-899a-7fb7cb377d05", - "name": "Carl-Gustaf Harroch" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 209173, + "id": 264357, "name": "Keynote" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185719, - "name": "Other" + "id": 264379, + "name": "Android Studio" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "e955b717-0b3b-4adc-8580-f5a7feb4135a", + "id": "6cf72726-82b7-4eb4-8be1-93a3ecd92823", "title": "Break", "description": null, - "startsAt": "2023-10-26T10:05:00", - "endsAt": "2023-10-26T10:30:00", + "startsAt": "2024-10-31T10:00:00", + "endsAt": "2024-10-31T10:20:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "530106", - "title": "Building Compose APIs", - "description": "...you won't believe how easy this is! Let's look at challenges you might encounter on your journey of building an API (in Jetpack Compose).\r\n\r\nThrough the example of Jetpack Compose's AnchoredDraggable API, we will build the most basic version of the component and iterate. What functionality do you need? How do you create a concise, easy-to-understand component? What layering do you want to strive for, and how do you incorporate feedback from developers?\r\n\r\nYou will take away a solid understanding of what building an API entails, an example API design process as well as Compose-specific design details and how this maps to non-library codebases.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "736883", + "title": "Optimizing Kotlin Code in Practice", + "description": "In this session we will look at a series of optimizations that were done in Jetpack Compose to learn how different types of optimizations can affect performance. These optimizations include code flow/algorithms, data structures, low-level bytecode optimizations, and memory optimizations. You will learn how to access and understand the bytecode and the machine code that your Kotlin code produces, so you can discover how to improve your applications on your own.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", - "name": "Jossi Wolf" + "id": "152f613f-841b-407d-9a65-3703ec2dfae2", + "name": "Romain Guy" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185724, - "name": "API" + "id": 264359, + "name": "Kotlin" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "1cad5d76-2e07-4cf9-87b3-f040b5c44c56", + "id": "29989aaa-e191-481d-bce4-3da2753edb6e", "title": "Break", "description": null, - "startsAt": "2023-10-26T11:10:00", - "endsAt": "2023-10-26T11:25:00", + "startsAt": "2024-10-31T11:00:00", + "endsAt": "2024-10-31T11:15:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "538649", - "title": "Migrating to Jetpack Compose - an interop love story", - "description": "Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you then.\r\n\r\nWhile Compose-only is a dream experience, the reality is that existing apps will be mixed Views and Compose for a long time. The most common migration strategy is: all new features written in Compose, while old code remains in Views. \r\n\r\nDevs do refactor old code as well - but it’s not a must. You don’t need a Compose-only app to reap the benefits of Compose. So, let’s embark on a journey of partially migrating a View sample, to test a few hypotheses:\r\n\r\n- “Migration of common UI first” strategy\r\n- View and Compose interop capabilities\r\n- Migrating to Compose while keeping original navigation\r\n- Redesigning a feature/screen? Perfect time to migrate!\r\n- Migrating to Compose? Perfect time to add large screen support!\r\n- Views to Compose don’t always need a 1:1 mapping", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "742198", + "title": "Demystifying Code Signing", + "description": "All Android apps have to be code signed. This is performed either by the developer or through Google Play Signing. Despite this, code signing and the processes around it are shrouded in mystery.\r\n\r\nDrawing from lived experiences, this talk with cover:\r\n - What is a code signature?\r\n - Why do we need to sign in the first place?\r\n - Developer signing vs Google Play Signing\r\n - Security expectations vs reality\r\n - Going beyond signing", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", - "name": "Simona Milanovic" + "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", + "name": "Neal Michie" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185713, - "name": "Compose" + "id": 264372, + "name": "Security" }, { - "id": 185735, - "name": "Android" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185741, - "name": "UI/UX" + "id": 264379, + "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "cc54e1b9-b1bc-42ae-9ad7-d2a8e6aa24aa", + "id": "9f104c35-655f-4d08-8752-1bddf514da82", "title": "Break", "description": null, - "startsAt": "2023-10-26T12:05:00", - "endsAt": "2023-10-26T12:20:00", + "startsAt": "2024-10-31T11:35:00", + "endsAt": "2024-10-31T11:50:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "528710", - "title": "Go the extra mile for your media app on Android", - "description": "You have a great application that can read media like a video or audio, that’s great!\r\n\r\n\r\nBut maybe you can go a tiny bit further, to enable your user to have more features, and a more premium experience!\r\n\r\n\r\nA ton of tiny things are easy to do, and can leverage your app to go even further, for instance : \r\n* Downloadable content\r\n* Spatial Audio with Head Tracking\r\n* Enable HDR\r\n* Enable Picture in Picture\r\n* Support HALF_OPENED for Foldables\r\n* Support Android Auto, TV and Wear\r\netc..\r\n\r\nA lot of things to do that do not require a lot of development!\r\n\r\nThe goal of this session is to show you what you can add to your app media experience, and tell you how it’s easy to do!\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "729194", + "title": "What’s New in Compose Multiplatform - A Live Tour", + "description": "What if you could just… do iOS development? Kotlin and Compose Multiplatform make it possible!\r\n\r\nLive coding our way through the evergrowing ecosystem built by JetBrains, Google, and the wonderful Kotlin community, we’ll show you how you can pick and choose well-established tools and libraries that you already know from Android and use them to build cross-platform apps.\r\n\r\nThis includes the addition of new Jetpack libraries such as Navigation, ViewModel, Room, and more – and you don’t have to make any compromises when it comes to using platform capabilities, either! Using JetBrains Fleet throughout the demos, you’ll also see the tooling support you get when developing multiplatform applications.\r\n\r\nYou may not realize it yet, but you probably already know how to build apps with Compose Multiplatform – for Android, iOS, and beyond.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f3facba0-c34d-4c87-a7e7-a81a67c8029d", - "name": "Antoine Danois" + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" + }, + { + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185716, - "name": "Modularization" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185717, - "name": "Foldables" + "id": 264359, + "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264361, + "name": "KMP" }, { - "id": 185719, - "name": "Other" + "id": 264364, + "name": "Compose" }, { - "id": 185724, - "name": "API" + "id": 264380, + "name": "Tooling" }, { - "id": 185731, - "name": "Tooling" + "id": 264381, + "name": "Cross-Platform" }, { - "id": 185730, - "name": "Android Studio" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "b6c668c1-a377-4f40-923f-90310a20ba01", + "id": "46932aef-14c0-4b86-80c3-50646e240b66", "title": "Lunch", "description": null, - "startsAt": "2023-10-26T12:40:00", - "endsAt": "2023-10-26T13:50:00", + "startsAt": "2024-10-31T12:30:00", + "endsAt": "2024-10-31T13:40:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "538660", - "title": "Easy screenshot testing with Compose", - "description": "UI tests are an integral part of a good testing story, but they tend to be a source of flakes and can cause slow CI runs. With screenshot tests you can make multiple assertions at the same time, prevent regressions across multiple form factors and verify visual appearance of Compose UIs. In this talk we'll walk you through how screenshot testing works, how to manage golden images, how to create a strategy of what to test, which of the available solutions to go for to speed up your tests runs, and also we'll take a peek at the upcoming support in the Android Gradle Plugin and Android Studio.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "728763", + "title": "Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!", + "description": "Let's have fun with animations, gestures and sensors!\r\n\r\nUsing Compose Multiplatform, we'll go over how to create animations using gestures and sensor events for Android & iOS. We'll cover some basics like how to get the device motion and position information, how to track gestures in the screen, and how you can combine them with animations to have fun! \r\n\r\nAfter this talk, you'll have a better understanding on how to use the sensor frameworks, how to make your own gesture effects, and how to create interesting animations in an easy way.\r\n\r\nKeep it fun, keep it animated!", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", - "name": "Jose Alcérreca" + "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", + "name": "Nicole Terc" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264359, + "name": "Kotlin" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264364, + "name": "Compose" + }, + { + "id": 264384, + "name": "Android" }, { - "id": 185741, + "id": 264390, "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "14331e69-603a-42c2-83cc-dee009c659b3", + "id": "2f36980d-7a9d-4899-8e0b-9e1de74805e0", "title": "Break", "description": null, - "startsAt": "2023-10-26T14:30:00", - "endsAt": "2023-10-26T14:45:00", + "startsAt": "2024-10-31T14:20:00", + "endsAt": "2024-10-31T14:35:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "529442", - "title": "Advanced Android Canvas APIs", - "description": "Dive into the Android Canvas APIs, the underlaying Skia engine and how it powers both native Android and Jetpack Compose UIs. This talk offers a deep dive into drawing primitives, transformations, and custom views, showcasing how to craft dynamic and visually engaging user interfaces. Join me to uncover the secrets of advanced Canvas techniques that will elevate your app's visual appeal and user experience.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "734739", + "title": "Video Editing on Android with Media3", + "description": "Learn how to use Media3 Editing libraries to edit, trim, concatenate and apply effects to video frames on Android. \r\nImplementing video editing has always been a challenge for Android developers. Media3 is a jetpack library that is meant to make video editing easy, performant and reliable. \r\nIn this session you will learn about Transformer APIs, how to apply effects and concatenate multiple media files. ", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "71373c0f-53e8-4020-8d16-1bdfe603c3c7", - "name": "Markus Hintersteiner" + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264374, + "name": "API" }, { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "882a3337-793f-44e5-a298-ddf8cf6437a6", + "id": "b28b4077-1368-4b9a-9ea2-ebd718631283", "title": "Break", "description": null, - "startsAt": "2023-10-26T15:05:00", - "endsAt": "2023-10-26T15:20:00", + "startsAt": "2024-10-31T14:55:00", + "endsAt": "2024-10-31T15:10:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "508933", - "title": "Meet Jewel: create IDE plugins in Compose ✨", - "description": "Jetpack Compose is the declarative UI toolkit for Android that makes it easy to create beautiful, responsive apps. However, until recently, there was no easy way to use Compose to create IDE plugins without too many compromises. Jewel is a new library that solves this problem by providing a set of tools and components that make it easy to create Compose for Desktop-based plugins for Android Studio and IntelliJ IDEA.\r\n\r\nIn this talk, we will introduce Jewel and show you how to use it to create your own IDE plugins. The session will cover the following topics:\r\n\r\n* Why Compose makes a huge difference in IDE plugin development\r\n* How to set up Compose in your IDE plugin project\r\n* What's Jewel, a library implementing the IntelliJ themes and a composables\r\n* What are the tradeoffs in using Compose and Jewel\r\n* Case studies of existing implementations\r\n* What's the future for Jewel and Compose as a plugin UI framework\r\n\r\nThis talk is intended for Android developers who are familiar with Compose but have never created an IDE plugin because they don't wanna learn Swing. By the end of this talk, you will have a good understanding of how to use Jewel to create your own Compose-based IDE plugins.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "710572", + "title": "Rethinking Home - How testing techniques and code design reshaped the new Spotify Home feature", + "description": "Let's dive into the dynamic world of Spotify Home development, where scalability and excellency are key. In this talk we will learn about the relationship between good code design and testability. How these principles synergise to influence the new shape of core Home features. Moreover we will go through the value of different test types and learn insights on how to make code more testable as well as actionable strategies to enhance code design in general ", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", - "name": "Sebastiano Poggi" + "id": "6ee5ee5a-1bdd-4c54-8178-fad51f86a78a", + "name": "Daniel Horowitz" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 }, { - "id": "eb4483f1-306b-447d-9888-648ec0fc0018", - "name": "Chris Sinco" + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264368, + "name": "Techniques & Guides" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "5264a86b-3452-40bf-b481-44b538e30c10", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T15:50:00", + "endsAt": "2024-10-31T16:05:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "714221", + "title": "Jetpack Compose: Drawing without pain and recomposition", + "description": "This is a talk on recomposition in Jetpack Compose and the myths of too many calls it is followed by. I'll briefly explain the reasons behind recompositions and why they are not as problematic as they may seem. I have prepared numerous examples that illustrate how to minimize its occurrence.\r\n\r\nI'll share real-life situations we encountered during the redesign of our main screen. I'll delve step-by-step into how I optimized a particle animation without additional memory allocation and how we successfully reduced the number of recompositions on the screen. My practical guide on parameter tuning will be a great takeaway.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "34b0d49b-00dd-4dc1-ac1c-6de040e8a5b9", + "name": "Vitalii Markus" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185730, - "name": "Android Studio" + "id": 264364, + "name": "Compose" }, { - "id": 185741, - "name": "UI/UX" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "41914d30-f362-4008-9408-aa6c1e5a53a6", + "id": "afa619be-3074-4ba1-8e3b-e61c3475d25a", "title": "Break", "description": null, - "startsAt": "2023-10-26T16:00:00", - "endsAt": "2023-10-26T16:15:00", + "startsAt": "2024-10-31T16:45:00", + "endsAt": "2024-10-31T17:00:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "516527", - "title": "The terminal is your friend", - "description": "Command line skills are something we sometimes overlook as Mobile Engineers. Fancy tools and IDEs have made our lives very comfortable. We don’t need to spend a significant portion of our time writing commands in our systems like previous generations of Software Engineers and System Administrators.\r\n\r\nYet there are times when either the IDE has decided to go nuts or your version control tool is not syncing properly and you want to go one level down to see what is going on. Or maybe you just simply want to develop your CI skills which are heavy script and command-line based. \r\n\r\nIn this talk, I want to share some tips and tricks to help you level up your command-line skills and make the console a companion for your daily Android work.\r\n", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "771426", + "title": "#TheAndroidShow - Leadership Keynote", + "description": "Join members of the Android leadership team, including Matthew McCullough, Tor Norbye and Clara Bayarri for insights into what's new in Android", + "startsAt": "2024-10-31T17:00:00", + "endsAt": "2024-10-31T17:20:00", "isServiceSession": false, - "isPlenumSession": false, + "isPlenumSession": true, "speakers": [ { - "id": "95a6035d-1167-4cc1-9974-bc065a077893", - "name": "Enrique Ramírez" + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264357, + "name": "Keynote" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185731, - "name": "Tooling" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "ddd494e2-fd61-411c-89b1-c4d80ba80f36", + "id": "103ac95c-d0ef-4dad-aeed-14e8261c947f", "title": "Break", "description": null, - "startsAt": "2023-10-26T16:35:00", - "endsAt": "2023-10-26T16:50:00", + "startsAt": "2024-10-31T17:20:00", + "endsAt": "2024-10-31T17:30:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "538657", - "title": "Enhancing Jetpack Compose App Performance: Tools, Strategies and Optimizations", - "description": "Explore techniques to elevate Jetpack Compose app performance through measurement, debugging, and strategic optimization. Harness the capabilities of sophisticated tools, including Macrobenchmark and Android Studio's profiling suite, to pinpoint bottlenecks. Delve into the implementation and debugging of Baseline Profiles, which mitigate the impact of JIT on your code execution. Additionally, learn strategies to alleviate heavy UI rendering, ensuring a smoother user experience. Join us to gain actionable insights and practical skills for crafting high-performing Jetpack Compose apps that deliver seamless user experiences.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "771431", + "title": "#TheAndroidShow - Panel Discussion", + "description": "Join moderator Florina Muntenescu and members of the Android Developer Relations team as they field questions on all the latest in Android development.", + "startsAt": "2024-10-31T17:30:00", + "endsAt": "2024-10-31T18:15:00", "isServiceSession": false, - "isPlenumSession": false, + "isPlenumSession": true, "speakers": [ { - "id": "c95ac59a-7c1e-45b6-ba71-cf6ed861ac4a", - "name": "Tomáš Mlynarič" + "id": "752bc716-f1b8-45d4-b0a5-cfbc3402b68b", + "name": "Florina Muntenescu" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" + }, + { + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "0e2cc89b-b559-4432-8df6-6e880873a92f", + "title": "Karaoke and Halloween Costume Party!", + "description": null, + "startsAt": "2024-10-31T18:15:00", + "endsAt": "2024-10-31T19:35:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false } ], "hasOnlyPlenumSessions": false }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "sessions": [ { - "id": "522881", - "title": "Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love", - "description": "Kotlin Multiplatform Mobile (KMM) is awesome for us Android Developers. Writing multiplatform code with it doesn't diverge much from our usual routine, and now with Compose Multiplaform, we can write an entire iOS app without knowing a thing about iOS development. But to truly conquer the world of mobile development, we need to bring the iOS developers to our side. Join us in this exciting presentation where we unravel the secrets of crafting Kotlin Multiplatform libraries that your iOS teammates won’t believe they weren’t written in Swift. Discover how Kotlin constructs are translated into Objective-C/Swift and explore effective strategies for addressing any translation problems. Learn tricks that ensure iOS developers will enjoy a frictionless experience while consuming KMM APIs. By using KMM one can significantly reduce development time and bug count.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "731970", + "title": "Wireless protocols for the next generation IoT devices", + "description": "In an era where mobile apps and IoT devices are becoming increasingly interconnected, understanding the nuances of modern wireless protocols is crucial for software developers. This technical talk aims to delve into the latest advancements and practical applications of wireless communication technologies, including Bluetooth Low Energy (BLE), WiFi, Ultra-Wideband (UWB), Thread, LoRaWAN, and more. Learn when and how to use each protocol and their pros and cons. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "2e8bbf8c-544f-460d-8145-78f6693c8ee4", - "name": "André Oriani" + "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", + "name": "Erik Hellman" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264369, + "name": "Other" }, { - "id": 185710, - "name": "KMP" + "id": 264370, + "name": "IoT" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264371, + "name": "Wearables" }, { - "id": 185732, + "id": 264381, "name": "Cross-Platform" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "501854", - "title": "Developer Productivity On a Budget", - "description": "Adam and Jordan both work on Platform teams at different companies supporting many distributed teams that contribute to their Android codebases. \r\n\r\nWe'd like to share some of the things we've built for our Engineers to help them become more productive and ensure that they deliver great experiences for our users.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "735932", + "title": "Don't Build to Last: Strategies for Designing and Productionising Experiments", + "description": "Striking the right balance between rapid iteration and long-term feature quality can feel like an impossible task. In this session, we'll explore how to move fast without breaking things, and delve into the art of \"productionising\" experimental code.\r\n\r\nThe session will navigate the entire lifecycle of experimentation from an Android engineers perspective. You'll learn how to work with your data team to design high-quality experiments with tight feedback loops and how to effectively roll them out - whether that involves making new behaviour permanent or cleaning up the mess. This session offers practical insights for engineering teams looking to innovate quickly while maintaining a robust production environment.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", - "name": "Adam Ahmed" - }, - { - "id": "1849dced-2e7b-4d3a-b8f9-3c6e9a62df29", - "name": "Jordan Terry" + "id": "85f45c57-98a0-45e9-8a5f-ed6096e268b9", + "name": "Kate Moksina" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, + "id": 264363, "name": "Testing" }, { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185724, - "name": "API" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185731, + "id": 264380, "name": "Tooling" }, { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185729, - "name": "Gradle" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530078", - "title": "Think outside the server", - "description": "Securing your server API is a common method to safeguard your Android application, however, it may not always be sufficient. In certain situations, you may need to have additional measures in place to protect the mobile app client beyond just server-side security.\r\n\r\nIn this presentation, we will explore a few of these scenarios and why they are so important to be aware of. In conclusion of this presentation, you will be able to identify, understand, and deal with the common client-side security challenges that you may encounter.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "760426", + "title": "Enter the Metaverse: Android Apps on Meta Horizon OS", + "description": "Meta Horizon Store is open for business to mobile developers. Android developers can now port existing mobile apps, easily add spatial capabilities and publish them for Meta Quest headsets. Start building for the next computing platform to target new users, innovate with mixed reality and grow your audience. With new tooling like Meta Spatial SDK, you can combine the rich ecosystem of Android development and the unique capabilities of Meta Quest via accessible APIs, all while using the mobile development languages, tools, and libraries you’re already familiar with.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5dd21448-f468-44ad-9220-75d25e8e2370", - "name": "James Hamilton" + "id": "555e52b7-fcb6-49ea-81c7-d9b52cc4c7e9", + "name": "Jonathan Wendorf" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" + "id": 264383, + "name": "AR/VR" }, { - "id": 185724, - "name": "API" - }, - { - "id": 185728, - "name": "Modern Android Development" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530107", - "title": "Threat Landscapes, Threat Models, and Threat Prevention: A Practical Guide for Android Developers", - "description": "Mobile apps are under constant attack. And the attackers' targets are not always the ones you might expect.\r\n\r\nJoin us for a whirlwind tour of the Android threat landscape, and discover some practical insights into what you can do to to fortify your apps:\r\n\r\n- Watch some real-time exploits of a demo banking app and start thinking more like an attacker\r\n- Get some tips on creating a threat model for your apps\r\n- Explore the protection measures that can help your apps to defend themselves", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "716104", + "title": "Swift Cheatsheet for Android/Kotlin Developers", + "description": "Knowing common Swift patterns and how they translate to Kotlin can help us understand better what the code does. Whether to see how some feature is implemented on the neighbor platform, perform code reviews, review or write tech specifications/proposals, or work with Kotlin Multiplatform.\r\n\r\nWe will go over some of the basics of the Swift language and how it compares to Kotlin. Additionally, we will cover common patterns that you might find in a typical iOS project like optional bindings, dictionaries, extensions, structures, and protocols.\r\n\r\nLeave this talk confident you can read, understand, and review Swift/iOS code. It will also help you start with Kotlin Multiplatform where knowledge of Swift and SwiftUI is important.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e98ce7fa-cb48-4eb6-8a60-be9d4b4c0b6a", - "name": "Ivan Kinash" - }, - { - "id": "e99fa2a9-4e01-4534-907d-00dbb1447360", - "name": "Ben Thompson" + "id": "c24d26ff-8a59-4954-b768-fcd09d8bb0a3", + "name": "Domen Lanišnik" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" - }, - { - "id": 185735, - "name": "Android" + "id": 264359, + "name": "Kotlin" }, { - "id": 185744, - "name": "sponsor" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530222", - "title": "App architecture - singular vs plural", - "description": "A single architecture is usually at the core of our apps. We build our app and its features around it and by its rules. And while our chosen architectural pattern might work, and even work well, do we really benefit from a \"one per app\" approach? \r\nIn this talk, we'll do a quick run-through of the most popular architecture patterns in Android and try to figure out if it's better to strive for a singular architecture per app or if we should consider different ones for different features.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "732898", + "title": "Your app could use a secondary screen!", + "description": "Learn how an Android application could be used in a desktop-style environment while leveraging a secondary screen. We'll go through some key technical aspects to consider while developing an enterprise-oriented application and how Zebra is helping the developers go beyond what Android already offers.\r\n\r\nKey takeaways:\r\n- How an Activity is getting Multi Window support\r\n- Lifecycle impacts while using the application on 2 screens\r\n- Working with DisplayManager APIs to launch your activity on a secondary screen", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "163e9ce7-9042-4482-8f9e-f713d92ceae4", - "name": "Yoni Tietz" + "id": "de4da391-af75-405b-8816-186c5d4e8fde", + "name": "Daniel Neamtu" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - }, - { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "524023", - "title": "Working with custom Android devices", - "description": "Android is now well established outside the world of smartphones. Although usually not officially supported, you can find Android running on all kinds of devices, like home appliances, digital signage, and point-of-sales terminals. Being Android developer today means you are likely to encounter these custom devices in your career.\r\n\r\nIn this session you will learn the most important parts of working on these devices. We will cover things like developers boards, board support packages, AOSP builds, firmware updates, and much more.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "751161", + "title": "Designing scalable Compose APIs", + "description": "As more and more apps and teams migrate to Compose, it's important to establish clear guidelines for writing high-quality Compose code. This talk will cover best practices and guidelines for developing idiomatic Compose APIs, with topics such as how to think about and plan for your components, how to leverage Kotlin and naming conventions, and define a solid structure of your component, and finally how to verify and maintain these APIs. We'll discuss the rationale behind these guidelines and how they can help developers write code that is more scalable, performant, and consistent.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", - "name": "Erik Hellman" + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185731, - "name": "Tooling" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264364, + "name": "Compose" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "516305", - "title": "Designing for Errors in a Kotlin-first SDK: When to throw, nullify, or return a Result.", - "description": "Error propagation is an important consideration for building feature-rich API architecture in Kotlin. Whereas apps may have the flexibility to add external dependencies and laser-focus on specific use-cases, designing an API requires minimal dependencies and wide applicability. This lightning talk is a crash course in Kotlin error propagation strategies and a case study in our experience designing errors when migrating our SDK from Java to Kotlin. We will go over the various techniques that Kotlin natively provides for us, including throwing exceptions, returning null, sealed classes, and Result, and how we decided which technique to use for our functions.", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "708032", + "title": "DIY Server-Driven UI: Building Auto Trader's SDUI platform", + "description": "Join us as we take you through Auto Trader’s 4-year journey in crafting our hybrid Server-Driven UI framework (Composable) and the architecture that powers our mobile apps. We’ll delve into why we decided to build instead of buy, our choice of Kotlin and the designs that fuelled our success. From pitfalls to breakthroughs, this talk will be full of useful insights for anyone considering building SDUI themselves.\r\n\r\nYou should leave this talk with the following:\r\n- Insights into our approach to SDUI\r\n- Building with a small team without stopping feature work.\r\n- Kotlin and Android tech insights of the system.\r\n- How we're leveraging KMP to improve client logic.\r\n- 4 years of learnings, did we make the right choice?\r\n- Tips and tricks for anyone considering the same.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5b17491b-317a-4fec-8b63-3264bc0a9cb9", - "name": "Hudson Miears" + "id": "18ce6325-d3f4-4478-99d0-5683c4b924c2", + "name": "Jimmy Ray" + }, + { + "id": "0233c2ee-3bcd-4dee-9de5-c765b8c27cc0", + "name": "Harriet Taylor" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264361, + "name": "KMP" }, { - "id": 185724, - "name": "API" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" }, { - "id": 185735, - "name": "Android" - }, - { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - }, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 49272, + "name": "Things", + "sessions": [ { - "id": "530024", - "title": "Put Your Tests on a Diet: Testing the Behavior and Not the Implementation", - "description": "How do you write tests? How much time do you spend writing tests? And how much time do you spend fixing them when refactoring?\r\n\r\nA few short years ago, we would test a class in JUnit by stacking one test after the other, mocking each one of its dependencies. But for large apps, writing tests in this way without any consideration of architecture can easily become a bottleneck.\r\n\r\nFor a task that takes so much of our time every day as developers, there is surprisingly little discussion online about optimal test design. At Perry Street Software, publisher of two of the world's most popular LGBTQ+ dating apps, after struggling writing and maintaining our unit tests in the past, we have spent much time developing what we like to call the Unit Testing Diet.\r\n\r\nOur Unit Testing Diet provides a healthy way to test and refactor an app at scale, by testing the behavior that a user performs and the outcome that they will perceive, without testing implementation details.\r\n\r\nJoin this talk to learn how to:\r\n\r\n1. Test your ViewModels, UseCases, and Repositories in a Given-When-Then style, without using mocks\r\n2. Test the behavior and not the implementation details\r\n3. Refactor your code without breaking your tests", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "735950", + "title": "What's new in Android 15 Security?", + "description": "Audience: Android Developers\r\nLevel: Beginner/Intermediate (No previous security experience required)\r\n\r\nTalk Summary:\r\n\r\nIn \"What's New in Android 15 Security?\", we’ll guide Android developers through the essential security updates and restrictions introduced in Android 15. Starting with a brief recap of the key security changes from Android 14, we'll dive into the new restrictions and features in Android 15 that every developer should know:\r\n\r\n- Supporting Android 15’s Private Space: Discover how to enable your app for this new feature to enhance user privacy while managing the challenges of a new type of process termination.\r\n- New Mitigation for Task Hijacking: Explore the new security measures designed to prevent malicious background apps from bringing other apps to the foreground.\r\n- Safer Intents: Discover enhanced safeguards that make intents more secure and robust, minimizing the risk of data leaks and unauthorized access.\r\n- Further Foreground Service Restrictions: Understand the tightened restrictions on foreground services and how to adapt your app to comply.\r\n- Recap of Privacy Sandbox Updates: Review the Privacy Sandbox and its updates in Android 15, focusing on how these changes impact user data handling and ad targeting.\r\n\r\n\r\nKey Takeaways:\r\n- Overview of Security Changes: Gain a clear understanding of the security updates in Android 15 and their implications for app development.\r\n- Practical Compliance Tips: Learn actionable strategies to ensure your app remains compliant with the latest security restrictions when running and/or targeting Android 15.\r\n- Leverage New Features: Discover how to take advantage of Android 15’s new security features to improve your app’s security and promote user privacy.\r\n\r\nSpeaker Experience:\r\nWith over 10 years of experience as a Senior Android Developer, co-authoring the Android Security Cookbook, and delivering talks internationally on Android security, I bring a deep understanding of the platform's security evolution. This will be a fresh and engaging talk for Droidcon London 2024.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "85d2aab0-cf59-492c-83fb-ff0a8490fb85", - "name": "Stelios Frantzeskakis" + "id": "8b008502-24e7-4af7-a7a8-d92a7e683271", + "name": "Scott Alexander-Bown" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264372, + "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - } - ], - "hasOnlyPlenumSessions": false - }, - { - "id": 36718, - "name": "Liskov", - "sessions": [ + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": "519579", - "title": "Shipping production ready Rust for Android", - "description": "In this talk, you'll learn why Mozilla invested in Rust for Android and how we've built open-source libraries to make it easier for others to do so as well. You will also learn when not to use Rust and the challenges that still remain with reaching into native code from the JVM.\r\n\r\nThe Firefox Sync team at Mozilla has been shipping production Rust code in Firefox Android for a couple of years now. Rust provides value because it's performant, cross-platform and memory-safe. Delivering production-ready Rust libraries to Android was challenging, but we believe we've made it better!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "733188", + "title": "Improving App Performance With Custom Thread Pools", + "description": "In this presentation, I will discuss how to optimise the performance of an app by implementing a custom thread pool.\r\n\r\nThe audience will learn how to replace multiple thread pools created by libraries such as Coroutines, WorkManager, and OkHttp with a single, efficient custom thread pool. \r\n\r\nKey points include:\r\n\r\n1. Thread Pool Awareness: Understanding how various libraries create threads without awareness of existing threads and what the impact of that is on performance.\r\n\r\n2. Thread Pool Auditing: Demonstrating how to print to the LogCat a list of the currently running threads and showcasing a before and after.\r\n\r\n3. Performance Enhancement: Reducing RAM usage and improving startup times by consolidating thread pools.\r\n\r\n4. Custom Thread Pool Implementation: Demonstrating through code snippets how to create a Thread Pool and integrate it into OkHttp, Coroutines and WorkManager.\r\n\r\n5. Understanding The Differences Between Coroutines IO and Default Dispatchers: Using this knowledge to create a custom Thread Pool for IO work and CPU work.\r\n\r\nThis talk will provide concrete techniques for developers to enhance app performance, making it a standout topic for the conference.\r\n\r\nExperience Level:\r\n7 years experience as an Android Engineer, currently at Compare The Market for almost 4 years.\r\n\r\nI would rate myself as having intermediate knowledge on this topic, however it is a specialised topic.\r\n\r\nI have not given this talk anywhere, although I have presented this topic internally at Compare The Market.\r\n\r\nKeywords:\r\nasynchronous programming, thread pool, performance optimisation", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ce5f643d-771a-4b14-b504-329c50924cc3", - "name": "Tarik Eshaq" + "id": "e841b549-a549-420e-ba73-3039c567a609", + "name": "Tin Novakovic" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185719, + "id": 264369, "name": "Other" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264384, + "name": "Android" }, { - "id": 185739, - "name": "Libraries" + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529488", - "title": "A/B Test Best Practices and Fun Case Studies from Deliveroo", - "description": "At Deliveroo we run hundreds of A/B tests per year, and sometimes they return very unexpected results! Over the years, we’ve learned lots about running A/B tests, and today we’ll share some best practices for success. We’ll share some stories about some of the strangest results we’ve seen and by the end, we hope to convince you to start running A/B tests in your apps too! \r\n\r\nTopics we will cover:\r\nWhat is an A/B test?\r\nWhat are some of the most surprising results we have got from A/B tests at Deliveroo?\r\nWhat did we learn from those A/B tests?\r\nWhat are the best practices for running A/B tests?\r\nHow can I convince my boss that we should be running A/B tests at our company?\r\n\r\nCome along to find out if handling process death impacts how often people order food, and exactly how valuable your splash screen is!\r\n", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "736303", + "title": "Text in Compose: Beyond the Basics", + "description": "In this talk, we'll explore the intricacies of text rendering in Jetpack Compose. We'll delve into text layout and various text APIs to create visually appealing and interactive text-based interfaces.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "0ef1e55b-29b9-4c4b-938d-c59b6c8ea9d3", - "name": "Jamie Adkins" - }, - { - "id": "48a744a3-c5e5-40b7-8c64-eb134ff4dee7", - "name": "Edward Harker" + "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", + "name": "Anastasia Soboleva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185719, - "name": "Other" + "id": 264364, + "name": "Compose" + }, + { + "id": 264377, + "name": "Modern Android Development" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528347", - "title": "Become a hero to your DevSecOps team", - "description": "Get in front of API Security. Mobile apps are the front line of your company’s customer engagement. With the correct architecture and security, they can also become the first line of defence in protecting your company’s infrastructure. This presentation will take you through a worked example showing how an Android app can strengthen an organisation’s cyber-defense.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "748950", + "title": "How to Optimize, Validate and Deploy ML Models On Device", + "description": "In this workshop we address the common challenges faced by developers migrating AI workloads from the cloud to edge devices. Qualcomm aims to democratize AI at the edge, easing the transition to the edge by supporting familiar frameworks and data types. ​\r\n\r\nWe'll talk through why ML is best done on device and how to easily select a model for your use case, train (or fine-tune), and then compile for the device of your choice.\r\n\r\nWe'll walk through how to get started, iterate on your model and meet performance requirements to deploy on device! We'll show examples on how to optimize models and bundle the model into your application.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", - "name": "Neal Michie" + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264355, + "name": "Workshop" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185735, - "name": "Android" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495541", - "title": "Composing ViewModels: Breaking ViewModels into smaller self-contained UI models", - "description": "In early times of Android development we used to have big activities. All view and business logic were being written in activities. Then fragments were introduced so that activities started to become leaner. Then several architectural patterns have started to appear in the Android scene: MVP, MVVM, MVI, etc. While all these were happening, ViewModel like classes started to get bigger and bigger. They have become the new big activities. However, we haven't often considered splitting view models into manageable self-contained granular ones.\r\n\r\nViews are composable and they can be composed to build bigger views. What about introducing smaller ViewModels, namely UI models, and using them to build composable UI models/View Models? \r\n\r\nIn this talk, we will introduce the micro-feature architecture that leverages granular UI models. We'll demonstrate how this approach enables us to create leaner UI hosts by breaking down view models into smaller, self-contained logical UI models. Moreover, we'll explore the distinctions between a UI model and a Jetpack ViewModel. \r\n\r\nWe'll delve into the advantages offered by micro-feature architecture, particularly in terms of single responsibility, composability, reusability, and testability. \r\n\r\nBy the end of the talk, you'll have a comprehensive understanding of how adopting this architecture can significantly enhance your development process and empower you to build high-quality, scalable applications which has highly dynamic contextual screens. \r\n\r\nMicro-feature architecture is used in several screens of 2 big apps in production which are now being used by 10M+ users. We will be also sharing our learnings about using this approach in these apps. ", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "768482", + "title": "How to Optimize, Validate and Deploy ML Models On Device (Part II)", + "description": "We'll walk through the steps to bring your ML model on device. In this hands on section of the workshop we will demonstrate the end to end workflow for a sample use case, using Qualcomm AI Hub to optimize a model and deploy it on device. \r\n\r\nWe'll then help you get set up and walk through various examples on how to use Qualcomm AI Hub. The Qualcomm AI Hub team will be there to teach you the ins and outs, enabling you to use the platform and bring your ML use case on device quickly and easily.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5c3850ee-778a-4904-bd52-a3927ec54dc6", - "name": "Hakan Bagci" + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264355, + "name": "Workshop" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185735, - "name": "Android" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "502453", - "title": "I have a Date", - "description": "One common task in Android app development is working with Date object, and Kotlin provides several options for managing it. \r\n\r\nIn this talk, we will explore the different ways to represent and manipulate dates in Kotlin, including using the built-in java.util.Date class, the third-party Joda-Time library, and the java.time package introduced in Java 8. \r\n\r\nWe will also discuss best practices for formatting and displaying dates to the user, and handling time zones and daylight saving time. By the end of this talk, attendees will have a strong understanding of how to effectively manage dates in their Kotlin Android apps.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "745796", + "title": "Securing Android: Tackling Advanced Threats and Enhancing App Security", + "description": "What threats are Android apps dealing with these days? In this talk, we will look at the latest security challenges and the best ways to keep your apps safe from new threats.\r\nWhen companies rush to release apps, they often skip important security steps, which can lead to higher costs and more risks. It's important to build security into your app from the beginning. We will discuss common risks for Android apps, how to handle them, and the challenges developers and security experts face when trying to protect code, keys, and data.\r\n\r\nKey Takeaways:\r\n\r\n* Get up to speed on the latest security threats specifically targeting Android apps.\r\n* Explore the latest advanced rooting technique such as Magisk and app instrumentation and the best way to protect your app.\r\n* Explore the differences between shrinking/minification and advanced techniques for obfuscation and anti-tampering, including cryptographic key protection.\r\n* Discover how to embed security throughout the development process to minimise risks and build resilient apps.\r\n\r\n\r\n", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "4674e36f-50f8-4ffb-a486-679d730c759e", - "name": "Renaud Mathieu" + "id": "24eb82e3-3ea7-4b45-9ffb-d63cac580e88", + "name": "Mohamed Kerroumi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264372, + "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528396", - "title": "Android App Performance in a Nutshell", - "description": "This session will give you a head start with actionable advice on the journey to Inspect, Improve and Monitor your app's performance with the Android Performance development process. You'll learn the latest in Baseline Profiles, Perfetto Tracing and Benchmarking.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "736123", + "title": "How we build Spotify for Android Automotive", + "description": "As the automotive industry embraces newer technologies and more mainstream software tech stacks, the in-car entertainment experience is rapidly evolving into a living room on wheels. In this session, we will take you on the journey of bringing Spotify to Android Automotive OS.\r\n\r\nIn this session, we will cover:\r\n\r\n* What is Android Automotive OS? Discover which car manufacturers and models are currently supported, and the different flavours of Android Automotive around!\r\n* Learn about the specific development challenges we faced while building Spotify for Android Automotive, including using Media Center templates to build features, adapting to diverse hardware, and challenging emulator setups.\r\n* Get insights into the unique aspects of releasing an app in the automotive ecosystem, from testing to ensure compliance with automotive standards to coordinating with car manufacturers for software updates.\r\n\r\nPlease join us to discover how we’re making the Spotify experience accessible, enjoyable and safe for drivers and passengers, and the innovative solutions we’ve developed to overcome the challenges of this cutting-edge platform. \r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e869461e-b6fb-43e5-a89c-033da87008c0", - "name": "Ben Weiss" + "id": "9c8384de-140e-47e4-aeb1-c85faf090fc8", + "name": "Danielle Vass" + }, + { + "id": "0aef86d4-a69b-4a82-bd8b-5ee05a3e421d", + "name": "Walid Tazout" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, - "name": "Testing" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185730, - "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - }, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 53981, + "name": "Stadia", + "sessions": [ { - "id": "530221", - "title": "Structuring modules in an Android app project", - "description": "Let us suppose we have a multi-module project that builds faster than a monolith. \r\nWe have embraced separation of concerns and can now test a self-contained module without mocking the universe. \r\nWe are impressed with modularization, so we create more modules. \r\nThe question now is: How do we organize these modules?\r\n\r\nIn this session, we’ll dive into how many modules are too many in an Android app project and whether there can be too many. You’ll leave this talk with an understanding of how we can improve Android app modularisation to increase developer productivity, organize a clear project structure, access modules faster and assign ownership for better workflows. ", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "693733", + "title": "Hardware development with KMP and Compose Desktop", + "description": "Nowadays, Mobile software development focuses on efficient architecture, scalability and delightful UX. Those are quite important topics but sometimes we forget Mobile Development has its roots in Embedded Software Development, which was that branch of software development focused on running code on limited resource devices.\r\n\r\nBoth have diverged, one specialised in mobile apps for smartphones, while the other has transformed more into IoT, industrial and other types of solutions.\r\n\r\nThere was an attempt to bring both together with Android Things. It made Android app development skills transferable for hardware development, letting developers create apps with their existing knowledge and interact with the hardware through “high-level” driver APIs. Unfortunately, it was decommissioned by Google. \r\n\r\nWith the flourishing of technologies like KMP and Compose Desktop, the regular Android developer again has some transferable skills that can be used to develop apps for non-smartphone hardware. \r\n\r\nIn this session I will talk about how to develop a simple app for devices like the Raspberry Pi, covering details like interacting with GPIO, WIFI and Bluetooth communication as well as current issues and workarounds. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c35f2359-2c5c-4b88-a4bc-024a6c025ce4", - "name": "Kinnera Priya Putti" + "id": "95a6035d-1167-4cc1-9974-bc065a077893", + "name": "Enrique Ramírez" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264361, + "name": "KMP" }, { - "id": 185735, - "name": "Android" + "id": 264370, + "name": "IoT" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "517414", - "title": "And Gradle says: sharing is caring - Or why Gradle Plugins are all you need for your Configuration", - "description": "Have you ever been in dependency hell? Are you tired of copying and pasting your setup from one project to another? Do you wish there would be an easy way to share your configurations, workflows, dependencies? Say no more!\r\n\r\nBorn out of painful lessons, this talk will give you a crash course in how you can ship your setup easily to different projects by using the power of Gradle (Convention) Plugins, VersionCatalogs, etc. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "756181", + "title": "Fortify the Fort: Think outside the security", + "description": "Protection is deliberately included in everything you do with Android. Privacy is enabled through Android security. Like any other developer, you need to follow a shared responsibility security model, meaning your customers and users play an essential part in the overall security posture. This talk shows how a developer can follow simple guidelines to improve the application's security and stop unauthorised access.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "24ce44ea-d436-4476-88ac-08695de0acfa", - "name": "Matthias Geisler" + "id": "4b141461-befd-4bd3-83c9-1c1f7599a9c6", + "name": "Goran Minov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185729, - "name": "Gradle" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - } - ], - "hasOnlyPlenumSessions": false - }, - { - "id": 39640, - "name": "Hopper", - "sessions": [ + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": "529946", - "title": "Brick by Brick: Building Open Source libraries", - "description": "So you have an idea, and you want to publish a library for it? But where do you start? Doing Open Source is a fine art which requires skill you don’t easily learn on schoolbooks. Creating a new library is like building a new house that people want to live in: you need to start with a great foundation, build your inner walls and then add all the niceties that make your house the best place to live in. \r\n\r\nJoin us as we share our journey building Open Source Android libraries: we’ll start from tools to help you organize your code, we’ll learn how to publish your libraries publicly and how to effectively maintain them. Throughout this journey, we’ll share our experience maintaining popular Android libraries such as Detekt, Chucker and AppIntro.\r\n\r\nA house won’t be a home without someone living inside it, though. As your library won’t be a popular library without a strong community around it. So we’ll have the opportunity to share our insights on building strong communities around Open Source, getting developers together, finding new contributors and dealing with maintainer burnout.\r\n\r\nCurious to know how to build a shiny new library brick by brick? Then make sure to don’t miss out this talk, and we can’t wait to see what you all will be building!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "733000", + "title": "Exploring Android Accessibility Malware", + "description": "Join us in exploring two techniques Android malware uses, focusing on the dangerous combination of credential stuffing attacks and Accessibility Service abuse. We'll demonstrate how cybercriminals can exploit these vulnerabilities to launch large-scale attacks on user accounts across multiple applications.\r\nOur talk will walk you through:\r\n1. The mechanics of credential stuffing and how it exploits common user behaviors.\r\n2. How malware can abuse Android's Accessibility Service to automate malicious actions.\r\n3. A step-by-step demonstration of a proof-of-concept that combines these techniques.\r\n4. Clever methods cybercriminals use to conceal their activities from users.\r\n5. The broader implications of these threats for mobile app security.\r\n 1 of 3\r\nWe'll dive into why these attacks are increasingly prevalent and how they can be executed with alarming ease. By understanding the attacker's perspective, we aim to highlight the critical need for robust security measures in mobile applications. However, implementing such security measures can be challenging for developers, often requiring significant time, expertise, and resources. This is where innovative solutions become crucial. Recognizing this gap in mobile app security, Appdome provides comprehensive protection against these threats through zero-code integration, allowing developers to secure their mobile apps effortlessly.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8253d551-1437-4a3e-b4f4-56bf73373109", - "name": "Paolo Rotolo" - }, - { - "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", - "name": "Nicola Corti" + "id": "36110e7b-c926-48ec-be43-b1a10e0641a0", + "name": "Gil Hartman" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264365, + "name": "Accessibility" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264372, + "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529968", - "title": "Remote Build Cache - overcoming the network latency tax", - "description": "Lengthy builds and tests are a tax on productivity, job satisfaction and CI infra costs. The solution is to modularise apps into sub-projects and connect build systems to a remote build cache. But in doing so how can we minimise the latency and bottlenecks of retrieving cache entries? This talk will show you how to turbo-boost your build benchmarks with remote caching.\r\n\r\n\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "725157", + "title": "Click-free releases & the making of a CLI app", + "description": "At TravelPerk, we've developed a tool that fully automates our release process, eliminating the need for human intervention. This was achieved using a lightweight Kotlin CLI app. In this talk, we'll provide an in-depth look at the design and implementation of this tool, and guide you through building your own Kotlin CLI app. Join me to explore the power of Kotlin and Gradle in creating robust, automated solutions.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3be14346-8241-4bd9-9a8e-6771276bc270", - "name": "Tamás Bazsonyi" + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264359, + "name": "Kotlin" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264380, + "name": "Tooling" }, { - "id": 185745, + "id": 264394, "name": "CI/CD" }, { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185729, + "id": 264378, "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "527630", - "title": "White-box Cryptography", - "description": "Dive into the world of white-box cryptography and discover its superior data protection compared to traditional methods. \r\n\r\nLearn how it guards against a range of threats including reverse engineering, and other common threats to applications. \r\n\r\nJoin us to elevate your security expertise and stay ahead in the ever-evolving landscape of data protection.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "734309", + "title": "Crafting Narratives: Shaping TalkBack with Compose Semantics", + "description": "\r\nThe focus of my talk will be on making modern, complex designs more intuitive for screen reader users using an empathetic approach . \u2028We will see how to create thoughtful and user-friendly narratives for accessibility by using Jetpack Compose semantics and optimizing the TalkBack experience.\r\n\r\nKey Takeaways:\r\n1. Creating Effective Narratives: Tips for simplifying complex UI elements.\r\n2. Testing Accessibility: Methods for validating and improving semantic changes.\r\n3. Designing for Complexity: Making complex UIs intuitive and accessible.\r\n4. Using Semantics Wisely: Enhancing accessibility with thoughtful design.\r\n", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c746f9bf-2963-4549-b2f8-26cdafa24936", - "name": "Oliver Williams" + "id": "bcf3c75a-e105-41de-8891-53f6b673e58a", + "name": "Sachin Sapkale" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185731, - "name": "Tooling" + "id": 264363, + "name": "Testing" }, { - "id": 185739, - "name": "Libraries" + "id": 264365, + "name": "Accessibility" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495134", - "title": "How to stop the ‘Gradle Snatchers’: Securing your builds from baddies", - "description": "Following on from one of the first recorded supply chain attacks against Gradle, this talk will discuss the security concerns surrounding our favorite build tool and how we can protect against them. This starts with gaining an understanding of some of Gradle's common vulnerabilities and how to avoid these within our Android projects. You'll leave this talk with:\r\n\r\n- Insights on the Gradle Wrapper supply-chain attack and how to protect against it.\r\n- An overview of a Gradle dependency attack and how to protect against them.\r\n- A concrete list of security setting best practices within Gradle, including wrapper verification, repository filtering, dependency verification and others.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "757989", + "title": "Rethinking Apps with Local-First Architecture", + "description": "Modern apps are expected to function flawlessly even in low-connectivity edge environments, but traditional centralized networking approaches often fall short. In this session, we’ll explore how we got here and where we’re headed, diving into the unique challenges of local-first architecture. We’ll look at some of the tough problems in this space and examine solutions that open new possibilities. By rethinking these aspects in your app, you can push the boundaries of what your app can do and create experiences that will amaze your users.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", - "name": "Ed Holloway-George" + "id": "da380fb8-c8ba-47e9-b224-26647296cff5", + "name": "Richard Das" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185735, - "name": "Android" + "id": 264369, + "name": "Other" }, { - "id": 185729, - "name": "Gradle" + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264388, + "name": "Libraries" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530160", - "title": "From Complex to Seamless - Succeeding in codebase migrations", - "description": "Complex changes require careful planning and execution - from gathering product requirements and system design to implementation. Introduce variables, like timezone differences, varying levels of technical and domain knowledge and a newly formed team and the complexity deepens. \r\n\r\nAt Skyscanner we were faced with all of these difficulties in our migration of our Hotels product from React Native to Native. This talk will take you through our journey and how we not only overcame these hurdles, but leveraged them to our advantage. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "732956", + "title": "Rewind and Resolve: A deep dive into building Session Replay for Android", + "description": "Understanding and debugging production issues can feel like solving a puzzle with missing pieces. We will dive deep into the journey of building a session replay feature for the Sentry Android SDK that helps developers capture those elusive bugs, respects user privacy, and maintains low overhead.\r\n\r\nIn this talk, we'll explore the technical aspects, including:\r\n\r\n- Taking screenshots of a Window\r\n- Redacting the screenshots to ensure no sensitive user data is leaked\r\n- Encoding the screenshots into a video\r\n- Collecting supporting data like breadcrumbs, network requests, logs, touches to improve debuggability\r\n- Maintaining low performance overhead while implementing all of the above.\r\n\r\nWe'll also demonstrate how our tool empowers Android engineers to gain actionable insights when solving errors, crashes, and ANRs.\r\n\r\nWhether you're an Android developer looking to improve your debugging toolkit or someone interested in the technical guts of the Android OS, the session will cover it all. Join us for a glimpse into the future of mobile debugging – it's a replay you won't want to miss!", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", - "name": "Maria Neumayer" + "id": "da013a11-7cb0-4b0d-a37e-e29ce98688e0", + "name": "Roman Zavarnitsyn" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "sessions": [ { - "id": "528351", - "title": "Becoming and Staying a Productive Developer with Build Scans, Build Validation Scripts and Gradle", - "description": "We will start by discussing some of the top Gradle issues that affect the productivity of Android developers. We will cover build cache misses and configuration cache issues as well as how to debug and solve them with various tools such as with the free Build Scan service, Android Studio debugger and also with Develocity.\r\n\r\nWe will then demonstrate combining the powerful Build Validation Scripts with automation and notifications to systematically surface new build cache misses and to keep them from reappearing once they have been fixed.\r\n\r\nWe will close by diving into the just released Artifact Transform insights in the free Build Scan tool. This is one of the most commonly requested features by Android developers. We will also close off by showing how you can use the new Tests API in Develocity to automate your flaky test workflows.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "711892", + "title": "Gradle: The Build System That Loves To Hate You", + "description": "Come to hear a talk about dozens of foot guns in Gradle that can make you tear your hair out as you try to \"just make it work\", presented by someone who has stepped on most of the rakes. For example, did you know every call in groovy is using reflection?!\r\n\r\nAlong with the issues you will also learn about way to avoid it or at least minimize your risk of misery. After the talk you should have more confidence as you venture into the land of Gradle build.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "89694c9d-5de0-4f39-a50a-ed48d6eb56ca", - "name": "Nelson Osacky" - }, - { - "id": "67c6035b-3961-48b0-b4f8-49593995c5a0", - "name": "Etienne Studer" + "id": "8e2c2869-4beb-4868-8651-8567219ea947", + "name": "Aurimas Liutikas" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185729, + "id": 264380, + "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264378, "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528793", - "title": "Sync Data with your Mobile Kotlin App The Easy Way!", - "description": "Part of building a great mobile app is syncing data between the app and your back end database. That used to mean creating and testing API calls, worrying about storing local data while offline, performing conflict resolution among multiple users, user authentication and authorization, network communications and error handling, and security. In this talk, I’ll show you how to address all of those issues effortlessly with MongoDB Atlas and Atlas for the Edge. Atlas is the Developer Data Platform from MongoDB – a multi-cloud database-as-a-service offering with state-of-the-art features to accelerate your development projects. Atlas for the Edge brings the power of MongoDB closer to your users at any location. Attendees will see how to build a mobile Kotlin app with the Atlas Edge SDK that easily connects with a MongoDB Atlas on the back end for fast bi-directional sync, out-of-the-box networking and conflict resolution, and built-in offline ACID-compliant data storage on the mobile device.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "735282", + "title": "Securing the Unseen: Advanced Threats, Best Practices & Challenges in Mobile App Security", + "description": "In today's app-driven world, securing mobile applications is not just an afterthought—it's a critical component of the development process. This session at DroidCon London will take you deep into the mobile app security world, focusing on the latest threats that target mobile applications and the advanced techniques used to secure them.\r\n\r\nWe'll explore cutting-edge security practices that should be integrated throughout the app development lifecycle, from secure coding standards to app security and comprehensive threat modeling. You'll learn to identify and mitigate vulnerabilities before they become exploits, ensuring your applications are resilient against modern attacks.\r\n\r\nWe'll discuss key risks, security techniques and challenges app developers and security engineers face when trying to secure code, keys, and data effectively.\r\n\r\nThis session is designed for app developers, security professionals, and penetration testers who want to stay ahead of the curve in mobile app security. Join us to enhance your skills and ensure your apps are secure in the face of today's and tomorrow's threats.\r\n", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "60004028-029a-4865-88eb-acd45debf033", - "name": "Mark Brown" + "id": "eddfc3bb-2258-45f8-b4a6-50233932a2d2", + "name": "Mohamed Kerroumi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264372, + "name": "Security" }, { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185744, - "name": "sponsor" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529504", - "title": "Supporting your architecture with code coverage and static analysis rules", - "description": "One challenge of engineering teams is how to ensure you're writing high-quality code. Code coverage has traditionally been one measure for this, but aiming for complete coverage across your codebase will rarely lead to meaningful results. \r\n\r\nIn this talk we'll look at how you can use inclusion and exclusion rules within your Android codebase to ensure that any measurements help enforce your architecture patterns, allowing engineers to focus on the code that matters and giving them confidence they're building in the right way.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "702107", + "title": "[REDACTED]: How to keep your app's secrets, secret", + "description": "Every app has secrets! These could be in many forms (no judgment here!) but in this talk, we'll focus on the most common use case of storing API keys or data in-app that we'd rather not make readily available to malicious actors.\r\n\r\nIn this talk, we'll look at answering one of the most asked questions in mobile security, \"How do I secure my API keys\" and ensure you have the knowledge and tools you need to do so.\r\n\r\nYou'll leave this talk with:\r\n- Actionable security best practices for securing your app\r\n- An idea of how to store API keys, at scale\r\n- Multiple examples of how to securely access your API keys\r\n- Some fun real-world examples of when things go wrong\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3e36c7fe-b7ed-4a1a-b197-5937e0c611f6", - "name": "Michael Tweed" + "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", + "name": "Ed Holloway-George" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264369, + "name": "Other" }, { - "id": 185731, - "name": "Tooling" + "id": 264372, + "name": "Security" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "535307", - "title": "What's new in Paparazzi?", - "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this talk, John will give an overview of how it works, followed by an update on what the Cash App Android team has been working on since then, including:\r\n\r\n* Resource and asset loading improvements\r\n* Video snapshot support\r\n* Snapshots from @Previews\r\n* Compose Multiplatform support\r\n\r\n...and more!\r\n\r\nBackground:\r\n\r\nPaparazzi is an Android testing library that allows you to render your application screens without a physical device or emulator!\r\n\r\nIt has been immensely improving the Android UI testing loop on Cash App and other well-known apps since 2019 by allowing you to refactor your screens with confidence while running blazingly fast on the JVM versus navigating on slow devices.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "756374", + "title": "Tools that could help you to target newer SDK level and improve compatibility", + "description": "It could be a painful journey when developers update the app's target SDK level. There could be new APIs, deprecation of current APIs and gated or non-gated change. \r\n\r\nThis session will share major changes in Android 15, the common mistakes when targeting the latest version of Android, why developers should target the latest version as soon as possible, and what tools are available to help developers.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", - "name": "John Rodriguez" + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185714, - "name": "Accessibility" + "id": 264384, + "name": "Android" }, { - "id": 185730, + "id": 264379, "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "540429", - "title": "REST in Peace: A Journey Through API Protection", - "description": "Isn't Droidcon a Mobile Developer's conference? So, why would I care about protecting REST APIs? Well, think twice! API protection starts in the app, as the protection level of the API will be only as strong as the protection mechanisms built in the app. Join us for this entertaining talk were we will cover the typical mechanisms used to protect REST APIs and how they can be \"exposed\" if insufficient protection is put into the application. And, of course, guide you into putting the right measures inside the app so that both, app and backend, are sufficiently protected.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "736362", + "title": "Streamlining Android app development and release processes", + "description": "Building, testing and releasing Apps for Android is unique with it's own challenges.\r\n\r\nDuring this session we’ll walk through three ways to make building, testing and releasing apps faster and easier for your whole team. Specifically, we’ll look at these three key points:\r\n\r\nAutomating testing and debugging by using CI/CD best practices and out of the box integrations.\r\n\r\nDelivering faster green builds with Bitrise Build Cache. We’ll look at some benchmarks and some real world examples of optimizing the execution.\r\n\r\nFrom beta distribution to production rollout, what steps are there and how these can be optimised.\r\n\r\nBy the end of this talk you'll have an idea on how to take the next steps on your CI/CD journey.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "377a3c7a-eba5-48e0-9057-0a5ff91061cc", - "name": "Marc Obrador Sureda" - }, - { - "id": "c6100c4b-13c4-433e-8dcd-ea0c92939179", - "name": "Andreas Luca" + "id": "3be14346-8241-4bd9-9a8e-6771276bc270", + "name": "Tamás Bazsonyi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264380, + "name": "Tooling" }, { - "id": 185724, - "name": "API" + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264384, + "name": "Android" }, { - "id": 185744, - "name": "sponsor" + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "510229", - "title": "Conducting tech interviews", - "description": "Conducting proper tech interviews are hard. It has taken me more than 20 years of interviews, both as a candidate and as an interviewer, to come up with a process and key aspects that I try hard to go through before hosting an interview. \r\nI've also learned as a candidate signs of an unhealthy company and a few take aways that I can apply to when I'm interviewing myself. Both positive and negative.\r\nIn this session I will share my history of technical interviews, what I look for when interviewing, how I want to set the scene, and how I try to always get the best out of each interview. Hopefully you can take away some learnings and inspiration for your own interviews after hearing me out. ", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "709487", + "title": "Dynamic Flow, the Wise approach to server driven UI", + "description": "In this presentation I will talk about how we combined the expertise of Android, iOS and Web engineers to create a server driven UI solution using Kotlin Multi Platform (KMP). This approach has allowed Wise to efficiently scale our product and business, resulting in more than 500 unique native screens being generated by the backend. \r\n\r\nLearn how this project has empowered backend engineers, streamlined UI development process and enhanced cross-platform collaboration.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bbcf99f4-d0a2-4081-afe8-114e09f54a86", - "name": "Bob Dahlberg" + "id": "f5e37102-a8be-4e34-be47-b98329cd2a23", + "name": "Phellipe Silva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" + "id": 264361, + "name": "KMP" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "504429", - "title": "Deep-Dive Into Mobile Accessibility & Interactive Quiz", - "description": "Nowadays mobile apps are at the heart of our interactions with the world around us. But are we sure that our apps are usable for everyone ? As developers, how can we make sure that the experience of our screen reader users is the best ?\r\n\r\nDuring this presentation, you will compete with the audience to measure your mobile accessibility knowledge, and learn at the same time how to optimize your application user experience for screen readers. \r\n\r\nWe will go deeper than the usual contentDescription to improve accessibility. For example by using Custom Action or Long Click to reduce the number of Talkback gestures needed to go through one screen. We will also open doors to new gestures to improve the UX of infinite horizontal lists like in Spotify or Netflix, that can be a trap for screen readers. \r\n\r\nPrepare yourself to learn new tricks that will make the usability of your app better for user with a screen reader than for sighted users.\r\n\r\nBonus 🎁 : The highest score to the quiz in the audience will win a small prize.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "736493", + "title": "Asynchrony and the infinite conveyor belt: Advanced coroutines and flows", + "description": "Most likely, you already know how to use coroutines and flows. They're part of our everyday existence. But all too often we learn them through repetition and rote learning, rather than forging a meaningful understanding of what's happening deep down. \r\nSo:\r\n\r\n- What *really* happens when you mark a function as 'suspend'?\r\n- How come crazy things like infinite loops on the main thread are possible?\r\n- What's the magic link between coroutines and flows?\r\n- What, really, is a scope? A context? A job?\r\n\r\nAnd there's a satisfying conclusion, too! We find that everything is linked: the path that seems to add complexity, in fact takes us back full circle.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "0e28eaf0-f8b6-4475-a3be-f3ce49f2d1ec", - "name": "Fanny Demey" + "id": "1fb7ac5d-d848-4d40-840e-d8ae38128ab4", + "name": "Tom Colvin" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" }, { - "id": 185714, - "name": "Accessibility" + "id": 264362, + "name": "Flow" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", "sessions": [ { - "id": "517498", - "title": "How Custom RenderObjects can make your life easier", - "description": "In Flutter we say: \"Everything is a Widget!\" and behind every visual widget there is at least one RenderObject.\r\n\r\nIf you already know the theory about RenderObjects, but you want to get your hands dirty and you don't know how to start, this talk is for you!\r\n\r\nDuring this session we will see in which use cases we want to create our own ones, which classes to use, how to understand how they work.\r\n\r\nI'm pretty sure that when we are not afraid of RenderObjects, they become our BFFFL (Best Flutter Friend For Life).", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "57d377d6-0328-445e-8212-70bacc1b8006", + "title": ".", + "description": null, + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "2167722e-e9a6-4cfd-8180-7436335316be", + "title": ".", + "description": null, + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "757537", + "title": "Roundtable: Mastering Release Management", + "description": "Teams of all sizes require release management processes that should ensure smooth delivery of app updates, while balancing the need for innovation with stability and user experience. Coordinating teams, managing release cycles, and mitigating risks while adapting to the Android ecosystem can be challenging. Join this open roundtable discussion to examine:\r\n•\tWhat are the biggest challenges in managing releases within the Android ecosystem, especially with the variety of devices and OS versions?\r\n•\tHow do you allow frequent releases while minimizing the toil of release management?\r\n•\tHow do you ensure a smooth release process while balancing the need for continuous innovation and feature updates?\r\n•\tWhat are some best practices for testing and quality assurance before rolling out an Android release to a large user base?\r\n•\tHow do you manage the potential impact of an unsuccessful or buggy release, and what steps are taken to mitigate such risks?\r\n•\tWhat role do user feedback and crash analytics play in refining subsequent Android releases?\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76117eec-d3f7-49c7-9684-c19058f241a0", - "name": "Romain Rastel" + "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", + "name": "Maria Neumayer" + }, + { + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528157", - "title": "Offline First Architecture with Bloc and Reactive Database", - "description": "Building apps that work flawlessly with limited network accessibility is not trivial. However, with more and more demanding users it's a necessity. There can be several approaches to offline first architecture and in this talk we'll look into one of them. You'll learn how to use flutter_bloc for handling user's actions and data, how to enable database to host persistent queue of actions, and resolve other challenging topics when the app is offline.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "757518", + "title": "Roundtable: EM or IC? What’s Right for Me?", + "description": "Choosing between a career path as an engineering manager or an individual contributor is a pivotal decision for engineers as they advance in their careers. This choice often hinges on personal interests, leadership aspirations, and the desire for hands-on technical work. Let’s get together and discuss:\r\n•\tWhat are the key factors that developers should consider when deciding between becoming an engineering manager or staying on the individual contributor track?\r\n•\tHow can someone assess whether they have the necessary skills and mindset to transition into a management role?\r\n•\tWhat are the most significant challenges and rewards that come with each path—engineering management versus individual contribution?\r\n•\tHow can companies support employees who are undecided or want to explore both career paths simultaneously?\r\n", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "29ba7916-20b4-48e5-9715-bddfbdd1aa3a", - "name": "Dominik Roszkowski" + "id": "af079576-c7db-497a-990d-919fc2472e45", + "name": "Anastasia López" + }, + { + "id": "f40ca183-824d-43d9-9e63-ee0edb5aa307", + "name": "Ataul Munim" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "606a45c5-6e96-49d9-b146-808283235582", + "title": ".", + "description": null, + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "517620", - "title": "Stop Treating Accessibility as an Afterthought: Concrete Steps to Build Inclusive Apps", - "description": "\u2028\u2028In today's digital world, accessibility is more important than ever. However, accessibility is often treated as an afterthought in the development process, which can lead to exclusion and frustration for users with disabilities. This talk aims to change that by discussing how to make accessibility a core part of your development workflow. We will cover topics such as collaboration within your team, available tools and when it makes sense to use them, and modifications to your project to ensure adding accessibility is a pain-free experience for feature development and testing. By implementing these strategies, you can create a more inclusive product and provide a better user experience for all.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "768515", + "title": "Kotlin by JetBrains, present and future", + "description": "Have a chat with Developer Advocates from JetBrains about anything Kotlin related on your mind. Language evolution, tooling, development practices, Kotlin Multiplatform and Compose Multiplatform... We're here for all of it!", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8e7913b6-fce4-419a-b862-7a1b535d1af9", - "name": "Manuela Sakura Rommel" + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" + }, + { + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "517499", - "title": "Exploring Records and Patterns", - "description": "Dart 3 is packed with new experimental language features, including Records, Pattern Matching, Algebraic datatypes, and Destructuring. Like Extensions, they’ll open up new opportunities for packages and ways to express oneself in code. Through concrete examples, Pascal will guide you through the syntax of these new features and demonstrate how they can be used to create more expressive and maintainable code.\r\n\r\nYou’ll learn about:\r\n\r\n- The benefits of the new Dart 3 language features\r\n- The considerations in designing these features\r\n- What the syntax looks like in isolation\r\n- New patterns and anti-patterns and how they will shape our Dart code of tomorrow\r\n\r\nWhether you're a seasoned Dart developer or just getting started, this talk will help you get up to speed on the latest advancements in the language. Don't wait for the future to arrive – start exploring these new features today and take your code to the next level.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "757517", + "title": "Roundtable: Community-Driven Development: How to Contribute to OSS", + "description": "Contributing to and maintaining open-source libraries in Android is a rewarding but challenging endeavor that benefits the developer community while enhancing personal skills. It involves code contributions, issue resolution, and ensuring the library remains stable and relevant over time. Join us as we have an open discussion about:\r\n•\tWhat motivated you to start contributing to or maintaining an open-source library in the Android ecosystem, or how do you get started?\r\n•\tWhat are the biggest challenges you face when maintaining an open-source project, particularly when managing contributions from other developers?\r\n•\tHow do you ensure that an open-source library remains compatible and up-to-date with new Android releases and API changes?\r\n•\tWhat are some best practices for encouraging meaningful contributions and fostering an active community around your open-source project?\r\n•\tHow do you balance the time commitment of maintaining an open-source library with other professional or personal responsibilities?\r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f117aa78-7e11-451d-b7ba-fb19ac866f31", - "name": "Pascal Welsch" + "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", + "name": "Nicola Corti" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - }, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 54706, + "name": "Ask Android", + "sessions": [ { - "id": "529978", - "title": "Building RTL-Ready Flutter Apps", - "description": "Are you looking to expand your app's reach to the global market and support RTL languages? Building RTL apps can be complex, but it's essential to ensure a seamless experience for millions of Arabic, Hebrew, and Persian-speaking users.\r\n\r\nThis talk will cover Flutter's RTL implementation and internationalization support. You'll learn about important widgets like Directionality, TextDirection, MediaQuery, and EdgeInsets that are crucial for creating RTL-friendly user interfaces.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "764303", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "cae62422-4040-4130-b345-29c8cd65f41b", - "name": "Raouf Rahiche" + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" + }, + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530253", - "title": "Google Home, But Better: Smart Home Display with Flutter", - "description": "We may not have realized it yet, but Flutter could become the next big player on embedded devices.\r\n\r\nWe'll take a dive into running Flutter on embedded Devices and build our own Smart Home Display using Flutter, a \r\nRaspberry Pi and embedded Linux.\r\n\r\nWhat's currently supported and what custom Embedder can we use to achieve this? How do we communicate between Flutter and the embedded hardware? How can we integrate and use Flutter in the Maker-Community to open up to the world of IoT?\r\n\r\nThis talk aims to show the potential of Flutter on embedded devices, to give a practical guide on how to start developing Flutter with a Raspberry Pi on embedded Linux, and to showcase a project where Flutter connects to the Internet of Things.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "764328", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:00:00", + "endsAt": "2024-10-31T11:15:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e6858863-4e16-44ab-b2e0-22c940533654", - "name": "Moritz Theis" + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185720, - "name": "IoT" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "512823", - "title": "Latest State of Flutter Feature for Developing Multiple-Entry Android Application", - "description": "Exploration of latest state of the functionality in Flutter for developing multiple entry Android application. The feature allows developers to build single application with multiple entry points to gate/isolate specific features under exact entry, what can be useful for specific cases, like building POS-terminal app, where custom launcher is used for opening specific features of the app.\r\n\r\nKey takeaways:\r\n- From experiment to release\r\n- When and why to build Android version of Flutter application with multiple entry points.\r\n- How to make it work on Android\r\n- Potential limitations of this feature", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "764329", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "606a1cb6-edd2-42bd-8bd4-08a51ef41c7f", - "name": "Vadym Pinchuk" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "517710", - "title": "Introduction to Game Development with Flutter and Flame", - "description": "This session will be purely based on Game Development in Flutter with help of Flutter. \r\nI will start with a basic introduction to the Flame engine and some of the concepts of Game Development like Folder Structure, Game Loop, Collision Detection, adding objects, and giving a piece of awesome music to our game. \r\n\r\nIt will be a Shooting game or a Platformer game, but it will cover introductory to intermediate topics of Game Development in Flutter with Flame Engine. \r\n\r\nThis will really help users to explore the Game Development sector of Flutter and Flame.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "764330", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:35:00", + "endsAt": "2024-10-31T11:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "d3469e78-7d62-4945-be1f-b9b3cc4a134b", - "name": "Shree Bhagwat" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - } - ], - "hasOnlyPlenumSessions": false - } - ], - "timeSlots": [ - { - "slotStart": "07:30:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "ca7e33b6-296c-4183-a100-266e11372aa3", - "title": "Registration & Check-In", - "description": null, - "startsAt": "2023-10-26T07:30:00", - "endsAt": "2023-10-26T09:00:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": null - }, - "index": 0 - } - ] - }, - { - "slotStart": "09:00:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "1d7cbedc-b0c4-482c-8204-448935ab0f5c", - "title": "Welcome & Keynote", - "description": null, - "startsAt": "2023-10-26T09:00:00", - "endsAt": "2023-10-26T09:15:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": null - }, - "index": 0 - } - ] - }, - { - "slotStart": "09:15:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "529143", - "title": "Challenges, failures and lessons in 15 years of building on top of Android.", - "description": "15 years in Android development and a whole lot of fun—Carl , founder of Novoda and the guy (and friends) who kickstarted droidcon London, invites you to a whirlwind ride down memory lane. it's a celebration of Android, the community, and the crazy roller coaster of a journey they've been on together.\r\n\r\nGet ready for:\r\n\r\n- A trip back to Android's 'Wild West' days—remember when everyone was figuring it all out?\r\n- A toast to droidcon London, the event that became the go-to hub for Android aficionados.\r\n- Real talk on building Android teams—because, there has been a bit of learning ", - "startsAt": "2023-10-26T09:15:00", - "endsAt": "2023-10-26T10:05:00", - "isServiceSession": false, - "isPlenumSession": true, - "speakers": [ - { - "id": "c74036f4-aebb-4fae-899a-7fb7cb377d05", - "name": "Carl-Gustaf Harroch" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185699, - "name": "Introductory and overview" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 209173, - "name": "Keynote" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], - "sort": 4 - } - ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 0 - } - ] - }, - { - "slotStart": "10:05:00", - "rooms": [ + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "e955b717-0b3b-4adc-8580-f5a7feb4135a", - "title": "Break", - "description": null, - "startsAt": "2023-10-26T10:05:00", - "endsAt": "2023-10-26T10:30:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": null - }, - "index": 0 - } - ] - }, - { - "slotStart": "10:30:00", - "rooms": [ + "id": "764323", + "title": "Ask Android! - Compose, KMP, Gemini in AS (copy)", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "530106", - "title": "Building Compose APIs", - "description": "...you won't believe how easy this is! Let's look at challenges you might encounter on your journey of building an API (in Jetpack Compose).\r\n\r\nThrough the example of Jetpack Compose's AnchoredDraggable API, we will build the most basic version of the component and iterate. What functionality do you need? How do you create a concise, easy-to-understand component? What layering do you want to strive for, and how do you incorporate feedback from developers?\r\n\r\nYou will take away a solid understanding of what building an API entails, an example API design process as well as Compose-specific design details and how this maps to non-library codebases.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ - { - "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", - "name": "Jossi Wolf" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185700, - "name": "Intermediate" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185704, - "name": "Session" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185724, - "name": "API" - } - ], - "sort": 4 - } - ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 0 + "id": "764331", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T12:30:00", + "endsAt": "2024-10-31T13:40:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "31e8a089-eb2f-437c-ac09-bb55af17f934", + "name": "Donovan McMurray" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": 36717, - "name": "Hamilton", - "session": { - "id": "522881", - "title": "Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love", - "description": "Kotlin Multiplatform Mobile (KMM) is awesome for us Android Developers. Writing multiplatform code with it doesn't diverge much from our usual routine, and now with Compose Multiplaform, we can write an entire iOS app without knowing a thing about iOS development. But to truly conquer the world of mobile development, we need to bring the iOS developers to our side. Join us in this exciting presentation where we unravel the secrets of crafting Kotlin Multiplatform libraries that your iOS teammates won’t believe they weren’t written in Swift. Discover how Kotlin constructs are translated into Objective-C/Swift and explore effective strategies for addressing any translation problems. Learn tricks that ensure iOS developers will enjoy a frictionless experience while consuming KMM APIs. By using KMM one can significantly reduce development time and bug count.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ + "id": "764343", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764345", + "title": "Ask Android! - Compose, GenAI, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:20:00", + "endsAt": "2024-10-31T14:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764347", + "title": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "81ee4554-4839-4192-9a74-ecb32dff2819", + "name": "Alex Vanyo" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764350", + "title": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:55:00", + "endsAt": "2024-10-31T15:10:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "327f56b4-c631-4489-9590-37bdfc856e3a", + "name": "Miguel Montemayor" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764353", + "title": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764351", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:50:00", + "endsAt": "2024-10-31T16:05:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "d713390d-9628-412f-9778-9c45aeb6b13e", + "name": "Satish Shende" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764365", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764370", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:45:00", + "endsAt": "2024-10-31T17:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + } + ], + "hasOnlyPlenumSessions": false + } + ], + "timeSlots": [ + { + "slotStart": "07:45:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "26c67130-cbaa-40e1-8270-4daea240daa2", + "title": "Registration & Check-In", + "description": null, + "startsAt": "2024-10-31T07:45:00", + "endsAt": "2024-10-31T09:00:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + } + ] + }, + { + "slotStart": "09:00:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "f1e96bdd-8d47-4dd2-b814-1421662f1da1", + "title": "Welcome & Keynote", + "description": null, + "startsAt": "2024-10-31T09:00:00", + "endsAt": "2024-10-31T09:10:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + } + ] + }, + { + "slotStart": "09:10:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "731273", + "title": "Project Sparkles: how Compose is changing Android Studio", + "description": "We use Android Studio every day, and appreciate how its rich feature set makes our job easier. Most people know that Android Studio is built on the IntelliJ Platform, the same that underpins the popular IntelliJ IDEA from JetBrains, which has seen lasting success for over 20 years. It’s a solid, expansive, and by far the best foundation on which we could stand on to deliver Android-oriented goodies.\r\n\r\nHowever, some parts of the IntelliJ Platform show the signs of time; in particular its UI framework, Swing, is proving the most limiting, having been around for almost 30 years. Don’t get us wrong — it works, and the IDEs themselves prove you can ship complex UIs by using Swing on the IntelliJ Platform. But as we looked at how nice it is to develop UIs on Android by using Jetpack Compose, we thought: why don’t we do the same?\r\n\r\nEnter Project Sparkles, which aims at gradually introducing new high-quality, polished UI surfaces in Android Studio, developed in Compose for Desktop, with all the bells and whistles you can expect from a top-tier interface. In this talk, we’ll cover how Project Sparkles is impacting the development of Android Studio, addressing long-standing user feedback, and how we’re working together with other teams at Google and JetBrains to build a framework to make your favourite IDE even better and easier to understand.\r\n\r\nWe’ll demonstrate a few examples of features already shipping that are powered by Project Sparkles, explain what our goals and ambitions are, and even show some sneak peeks of things you may see in a future Studio version. UI enthusiasts, assemble!", + "startsAt": "2024-10-31T09:10:00", + "endsAt": "2024-10-31T10:00:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264357, + "name": "Keynote" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264379, + "name": "Android Studio" + }, + { + "id": 264390, + "name": "UI/UX" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 0 + } + ] + }, + { + "slotStart": "10:00:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "6cf72726-82b7-4eb4-8be1-93a3ecd92823", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T10:00:00", + "endsAt": "2024-10-31T10:20:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + } + ] + }, + { + "slotStart": "10:20:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "736883", + "title": "Optimizing Kotlin Code in Practice", + "description": "In this session we will look at a series of optimizations that were done in Jetpack Compose to learn how different types of optimizations can affect performance. These optimizations include code flow/algorithms, data structures, low-level bytecode optimizations, and memory optimizations. You will learn how to access and understand the bytecode and the machine code that your Kotlin code produces, so you can discover how to improve your applications on your own.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "152f613f-841b-407d-9a65-3703ec2dfae2", + "name": "Romain Guy" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 0 + }, + { + "id": 49271, + "name": "Glass", + "session": { + "id": "731970", + "title": "Wireless protocols for the next generation IoT devices", + "description": "In an era where mobile apps and IoT devices are becoming increasingly interconnected, understanding the nuances of modern wireless protocols is crucial for software developers. This technical talk aims to delve into the latest advancements and practical applications of wireless communication technologies, including Bluetooth Low Energy (BLE), WiFi, Ultra-Wideband (UWB), Thread, LoRaWAN, and more. Learn when and how to use each protocol and their pros and cons. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", + "name": "Erik Hellman" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264370, + "name": "IoT" + }, + { + "id": 264371, + "name": "Wearables" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 1 + }, + { + "id": 49272, + "name": "Things", + "session": { + "id": "735950", + "title": "What's new in Android 15 Security?", + "description": "Audience: Android Developers\r\nLevel: Beginner/Intermediate (No previous security experience required)\r\n\r\nTalk Summary:\r\n\r\nIn \"What's New in Android 15 Security?\", we’ll guide Android developers through the essential security updates and restrictions introduced in Android 15. Starting with a brief recap of the key security changes from Android 14, we'll dive into the new restrictions and features in Android 15 that every developer should know:\r\n\r\n- Supporting Android 15’s Private Space: Discover how to enable your app for this new feature to enhance user privacy while managing the challenges of a new type of process termination.\r\n- New Mitigation for Task Hijacking: Explore the new security measures designed to prevent malicious background apps from bringing other apps to the foreground.\r\n- Safer Intents: Discover enhanced safeguards that make intents more secure and robust, minimizing the risk of data leaks and unauthorized access.\r\n- Further Foreground Service Restrictions: Understand the tightened restrictions on foreground services and how to adapt your app to comply.\r\n- Recap of Privacy Sandbox Updates: Review the Privacy Sandbox and its updates in Android 15, focusing on how these changes impact user data handling and ad targeting.\r\n\r\n\r\nKey Takeaways:\r\n- Overview of Security Changes: Gain a clear understanding of the security updates in Android 15 and their implications for app development.\r\n- Practical Compliance Tips: Learn actionable strategies to ensure your app remains compliant with the latest security restrictions when running and/or targeting Android 15.\r\n- Leverage New Features: Discover how to take advantage of Android 15’s new security features to improve your app’s security and promote user privacy.\r\n\r\nSpeaker Experience:\r\nWith over 10 years of experience as a Senior Android Developer, co-authoring the Android Security Cookbook, and delivering talks internationally on Android security, I bring a deep understanding of the platform's security evolution. This will be a fresh and engaging talk for Droidcon London 2024.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "8b008502-24e7-4af7-a7a8-d92a7e683271", + "name": "Scott Alexander-Bown" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264372, + "name": "Security" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49272, + "room": "Things", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 2 + }, + { + "id": 53981, + "name": "Stadia", + "session": { + "id": "693733", + "title": "Hardware development with KMP and Compose Desktop", + "description": "Nowadays, Mobile software development focuses on efficient architecture, scalability and delightful UX. Those are quite important topics but sometimes we forget Mobile Development has its roots in Embedded Software Development, which was that branch of software development focused on running code on limited resource devices.\r\n\r\nBoth have diverged, one specialised in mobile apps for smartphones, while the other has transformed more into IoT, industrial and other types of solutions.\r\n\r\nThere was an attempt to bring both together with Android Things. It made Android app development skills transferable for hardware development, letting developers create apps with their existing knowledge and interact with the hardware through “high-level” driver APIs. Unfortunately, it was decommissioned by Google. \r\n\r\nWith the flourishing of technologies like KMP and Compose Desktop, the regular Android developer again has some transferable skills that can be used to develop apps for non-smartphone hardware. \r\n\r\nIn this session I will talk about how to develop a simple app for devices like the Raspberry Pi, covering details like interacting with GPIO, WIFI and Bluetooth communication as well as current issues and workarounds. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "95a6035d-1167-4cc1-9974-bc065a077893", + "name": "Enrique Ramírez" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264370, + "name": "IoT" + } + ], + "sort": 4 + } + ], + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 3 + }, + { + "id": 53982, + "name": "Nest", + "session": { + "id": "711892", + "title": "Gradle: The Build System That Loves To Hate You", + "description": "Come to hear a talk about dozens of foot guns in Gradle that can make you tear your hair out as you try to \"just make it work\", presented by someone who has stepped on most of the rakes. For example, did you know every call in groovy is using reflection?!\r\n\r\nAlong with the issues you will also learn about way to avoid it or at least minimize your risk of misery. After the talk you should have more confidence as you venture into the land of Gradle build.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "8e2c2869-4beb-4868-8651-8567219ea947", + "name": "Aurimas Liutikas" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264378, + "name": "Gradle" + } + ], + "sort": 4 + } + ], + "roomId": 53982, + "room": "Nest", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 4 + }, + { + "id": 54703, + "name": "Chromecast", + "session": { + "id": "57d377d6-0328-445e-8212-70bacc1b8006", + "title": ".", + "description": null, + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764303", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" + }, + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 + } + ] + }, + { + "slotStart": "11:00:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "29989aaa-e191-481d-bce4-3da2753edb6e", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T11:00:00", + "endsAt": "2024-10-31T11:15:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764328", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:00:00", + "endsAt": "2024-10-31T11:15:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 + } + ] + }, + { + "slotStart": "11:15:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "742198", + "title": "Demystifying Code Signing", + "description": "All Android apps have to be code signed. This is performed either by the developer or through Google Play Signing. Despite this, code signing and the processes around it are shrouded in mystery.\r\n\r\nDrawing from lived experiences, this talk with cover:\r\n - What is a code signature?\r\n - Why do we need to sign in the first place?\r\n - Developer signing vs Google Play Signing\r\n - Security expectations vs reality\r\n - Going beyond signing", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", + "name": "Neal Michie" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264353, + "name": "Lightning talk" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264379, + "name": "Android Studio" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 0 + }, + { + "id": 49271, + "name": "Glass", + "session": { + "id": "735932", + "title": "Don't Build to Last: Strategies for Designing and Productionising Experiments", + "description": "Striking the right balance between rapid iteration and long-term feature quality can feel like an impossible task. In this session, we'll explore how to move fast without breaking things, and delve into the art of \"productionising\" experimental code.\r\n\r\nThe session will navigate the entire lifecycle of experimentation from an Android engineers perspective. You'll learn how to work with your data team to design high-quality experiments with tight feedback loops and how to effectively roll them out - whether that involves making new behaviour permanent or cleaning up the mess. This session offers practical insights for engineering teams looking to innovate quickly while maintaining a robust production environment.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ { - "id": "2e8bbf8c-544f-460d-8145-78f6693c8ee4", - "name": "André Oriani" + "id": "85f45c57-98a0-45e9-8a5f-ed6096e268b9", + "name": "Kate Moksina" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185710, - "name": "KMP" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264380, + "name": "Tooling" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 1 + }, + { + "id": 49272, + "name": "Things", + "session": { + "id": "733188", + "title": "Improving App Performance With Custom Thread Pools", + "description": "In this presentation, I will discuss how to optimise the performance of an app by implementing a custom thread pool.\r\n\r\nThe audience will learn how to replace multiple thread pools created by libraries such as Coroutines, WorkManager, and OkHttp with a single, efficient custom thread pool. \r\n\r\nKey points include:\r\n\r\n1. Thread Pool Awareness: Understanding how various libraries create threads without awareness of existing threads and what the impact of that is on performance.\r\n\r\n2. Thread Pool Auditing: Demonstrating how to print to the LogCat a list of the currently running threads and showcasing a before and after.\r\n\r\n3. Performance Enhancement: Reducing RAM usage and improving startup times by consolidating thread pools.\r\n\r\n4. Custom Thread Pool Implementation: Demonstrating through code snippets how to create a Thread Pool and integrate it into OkHttp, Coroutines and WorkManager.\r\n\r\n5. Understanding The Differences Between Coroutines IO and Default Dispatchers: Using this knowledge to create a custom Thread Pool for IO work and CPU work.\r\n\r\nThis talk will provide concrete techniques for developers to enhance app performance, making it a standout topic for the conference.\r\n\r\nExperience Level:\r\n7 years experience as an Android Engineer, currently at Compare The Market for almost 4 years.\r\n\r\nI would rate myself as having intermediate knowledge on this topic, however it is a specialised topic.\r\n\r\nI have not given this talk anywhere, although I have presented this topic internally at Compare The Market.\r\n\r\nKeywords:\r\nasynchronous programming, thread pool, performance optimisation", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e841b549-a549-420e-ba73-3039c567a609", + "name": "Tin Novakovic" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264353, + "name": "Lightning talk" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264369, + "name": "Other" }, { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185737, + "id": 264386, "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 2 + }, + { + "id": 53981, + "name": "Stadia", + "session": { + "id": "756181", + "title": "Fortify the Fort: Think outside the security", + "description": "Protection is deliberately included in everything you do with Android. Privacy is enabled through Android security. Like any other developer, you need to follow a shared responsibility security model, meaning your customers and users play an essential part in the overall security posture. This talk shows how a developer can follow simple guidelines to improve the application's security and stop unauthorised access.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "4b141461-befd-4bd3-83c9-1c1f7599a9c6", + "name": "Goran Minov" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264353, + "name": "Lightning talk" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" + } + ], + "sort": 4 + } + ], + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 3 + }, + { + "id": 53982, + "name": "Nest", + "session": { + "id": "735282", + "title": "Securing the Unseen: Advanced Threats, Best Practices & Challenges in Mobile App Security", + "description": "In today's app-driven world, securing mobile applications is not just an afterthought—it's a critical component of the development process. This session at DroidCon London will take you deep into the mobile app security world, focusing on the latest threats that target mobile applications and the advanced techniques used to secure them.\r\n\r\nWe'll explore cutting-edge security practices that should be integrated throughout the app development lifecycle, from secure coding standards to app security and comprehensive threat modeling. You'll learn to identify and mitigate vulnerabilities before they become exploits, ensuring your applications are resilient against modern attacks.\r\n\r\nWe'll discuss key risks, security techniques and challenges app developers and security engineers face when trying to secure code, keys, and data effectively.\r\n\r\nThis session is designed for app developers, security professionals, and penetration testers who want to stay ahead of the curve in mobile app security. Join us to enhance your skills and ensure your apps are secure in the face of today's and tomorrow's threats.\r\n", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "eddfc3bb-2258-45f8-b4a6-50233932a2d2", + "name": "Mohamed Kerroumi" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264353, + "name": "Lightning talk" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264372, + "name": "Security" + }, + { + "id": 264382, + "name": "AI/ML" + } + ], + "sort": 4 + } + ], + "roomId": 53982, + "room": "Nest", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 4 + }, + { + "id": 54703, + "name": "Chromecast", + "session": { + "id": "2167722e-e9a6-4cfd-8180-7436335316be", + "title": ".", + "description": null, + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764329", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 + } + ] + }, + { + "slotStart": "11:35:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "9f104c35-655f-4d08-8752-1bddf514da82", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T11:35:00", + "endsAt": "2024-10-31T11:50:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 }, { - "id": 36718, - "name": "Liskov", + "id": 54706, + "name": "Ask Android", "session": { - "id": "519579", - "title": "Shipping production ready Rust for Android", - "description": "In this talk, you'll learn why Mozilla invested in Rust for Android and how we've built open-source libraries to make it easier for others to do so as well. You will also learn when not to use Rust and the challenges that still remain with reaching into native code from the JVM.\r\n\r\nThe Firefox Sync team at Mozilla has been shipping production Rust code in Firefox Android for a couple of years now. Rust provides value because it's performant, cross-platform and memory-safe. Delivering production-ready Rust libraries to Android was challenging, but we believe we've made it better!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "764330", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:35:00", + "endsAt": "2024-10-31T11:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ce5f643d-771a-4b14-b504-329c50924cc3", - "name": "Tarik Eshaq" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185719, - "name": "Other" - }, - { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 - }, + "index": 6 + } + ] + }, + { + "slotStart": "11:50:00", + "rooms": [ { - "id": 39640, - "name": "Hopper", + "id": 49270, + "name": "Hangouts", "session": { - "id": "529946", - "title": "Brick by Brick: Building Open Source libraries", - "description": "So you have an idea, and you want to publish a library for it? But where do you start? Doing Open Source is a fine art which requires skill you don’t easily learn on schoolbooks. Creating a new library is like building a new house that people want to live in: you need to start with a great foundation, build your inner walls and then add all the niceties that make your house the best place to live in. \r\n\r\nJoin us as we share our journey building Open Source Android libraries: we’ll start from tools to help you organize your code, we’ll learn how to publish your libraries publicly and how to effectively maintain them. Throughout this journey, we’ll share our experience maintaining popular Android libraries such as Detekt, Chucker and AppIntro.\r\n\r\nA house won’t be a home without someone living inside it, though. As your library won’t be a popular library without a strong community around it. So we’ll have the opportunity to share our insights on building strong communities around Open Source, getting developers together, finding new contributors and dealing with maintainer burnout.\r\n\r\nCurious to know how to build a shiny new library brick by brick? Then make sure to don’t miss out this talk, and we can’t wait to see what you all will be building!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "729194", + "title": "What’s New in Compose Multiplatform - A Live Tour", + "description": "What if you could just… do iOS development? Kotlin and Compose Multiplatform make it possible!\r\n\r\nLive coding our way through the evergrowing ecosystem built by JetBrains, Google, and the wonderful Kotlin community, we’ll show you how you can pick and choose well-established tools and libraries that you already know from Android and use them to build cross-platform apps.\r\n\r\nThis includes the addition of new Jetpack libraries such as Navigation, ViewModel, Room, and more – and you don’t have to make any compromises when it comes to using platform capabilities, either! Using JetBrains Fleet throughout the demos, you’ll also see the tooling support you get when developing multiplatform applications.\r\n\r\nYou may not realize it yet, but you probably already know how to build apps with Compose Multiplatform – for Android, iOS, and beyond.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8253d551-1437-4a3e-b4f4-56bf73373109", - "name": "Paolo Rotolo" + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" }, { - "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", - "name": "Nicola Corti" + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264361, + "name": "KMP" }, { - "id": 185735, - "name": "Android" + "id": 264364, + "name": "Compose" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264381, + "name": "Cross-Platform" }, { - "id": 185739, + "id": 264388, "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 0 }, { - "id": 39641, - "name": "Booth", + "id": 49271, + "name": "Glass", "session": { - "id": "528351", - "title": "Becoming and Staying a Productive Developer with Build Scans, Build Validation Scripts and Gradle", - "description": "We will start by discussing some of the top Gradle issues that affect the productivity of Android developers. We will cover build cache misses and configuration cache issues as well as how to debug and solve them with various tools such as with the free Build Scan service, Android Studio debugger and also with Develocity.\r\n\r\nWe will then demonstrate combining the powerful Build Validation Scripts with automation and notifications to systematically surface new build cache misses and to keep them from reappearing once they have been fixed.\r\n\r\nWe will close by diving into the just released Artifact Transform insights in the free Build Scan tool. This is one of the most commonly requested features by Android developers. We will also close off by showing how you can use the new Tests API in Develocity to automate your flaky test workflows.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "760426", + "title": "Enter the Metaverse: Android Apps on Meta Horizon OS", + "description": "Meta Horizon Store is open for business to mobile developers. Android developers can now port existing mobile apps, easily add spatial capabilities and publish them for Meta Quest headsets. Start building for the next computing platform to target new users, innovate with mixed reality and grow your audience. With new tooling like Meta Spatial SDK, you can combine the rich ecosystem of Android development and the unique capabilities of Meta Quest via accessible APIs, all while using the mobile development languages, tools, and libraries you’re already familiar with.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "89694c9d-5de0-4f39-a50a-ed48d6eb56ca", - "name": "Nelson Osacky" - }, - { - "id": "67c6035b-3961-48b0-b4f8-49593995c5a0", - "name": "Etienne Studer" + "id": "555e52b7-fcb6-49ea-81c7-d9b52cc4c7e9", + "name": "Jonathan Wendorf" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185729, - "name": "Gradle" + "id": 264383, + "name": "AR/VR" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 + "index": 1 }, { - "id": 39642, - "name": "Lamarr", + "id": 49272, + "name": "Things", "session": { - "id": "517498", - "title": "How Custom RenderObjects can make your life easier", - "description": "In Flutter we say: \"Everything is a Widget!\" and behind every visual widget there is at least one RenderObject.\r\n\r\nIf you already know the theory about RenderObjects, but you want to get your hands dirty and you don't know how to start, this talk is for you!\r\n\r\nDuring this session we will see in which use cases we want to create our own ones, which classes to use, how to understand how they work.\r\n\r\nI'm pretty sure that when we are not afraid of RenderObjects, they become our BFFFL (Best Flutter Friend For Life).", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "736303", + "title": "Text in Compose: Beyond the Basics", + "description": "In this talk, we'll explore the intricacies of text rendering in Jetpack Compose. We'll delve into text layout and various text APIs to create visually appealing and interactive text-based interfaces.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76117eec-d3f7-49c7-9684-c19058f241a0", - "name": "Romain Rastel" + "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", + "name": "Anastasia Soboleva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185727, - "name": "Flutter" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264377, + "name": "Modern Android Development" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "11:10:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "1cad5d76-2e07-4cf9-87b3-f040b5c44c56", - "title": "Break", - "description": null, - "startsAt": "2023-10-26T11:10:00", - "endsAt": "2023-10-26T11:25:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 - } - ] - }, - { - "slotStart": "11:25:00", - "rooms": [ + "index": 2 + }, { - "id": 36716, - "name": "Lovelace", + "id": 53981, + "name": "Stadia", "session": { - "id": "538649", - "title": "Migrating to Jetpack Compose - an interop love story", - "description": "Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you then.\r\n\r\nWhile Compose-only is a dream experience, the reality is that existing apps will be mixed Views and Compose for a long time. The most common migration strategy is: all new features written in Compose, while old code remains in Views. \r\n\r\nDevs do refactor old code as well - but it’s not a must. You don’t need a Compose-only app to reap the benefits of Compose. So, let’s embark on a journey of partially migrating a View sample, to test a few hypotheses:\r\n\r\n- “Migration of common UI first” strategy\r\n- View and Compose interop capabilities\r\n- Migrating to Compose while keeping original navigation\r\n- Redesigning a feature/screen? Perfect time to migrate!\r\n- Migrating to Compose? Perfect time to add large screen support!\r\n- Views to Compose don’t always need a 1:1 mapping", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "733000", + "title": "Exploring Android Accessibility Malware", + "description": "Join us in exploring two techniques Android malware uses, focusing on the dangerous combination of credential stuffing attacks and Accessibility Service abuse. We'll demonstrate how cybercriminals can exploit these vulnerabilities to launch large-scale attacks on user accounts across multiple applications.\r\nOur talk will walk you through:\r\n1. The mechanics of credential stuffing and how it exploits common user behaviors.\r\n2. How malware can abuse Android's Accessibility Service to automate malicious actions.\r\n3. A step-by-step demonstration of a proof-of-concept that combines these techniques.\r\n4. Clever methods cybercriminals use to conceal their activities from users.\r\n5. The broader implications of these threats for mobile app security.\r\n 1 of 3\r\nWe'll dive into why these attacks are increasingly prevalent and how they can be executed with alarming ease. By understanding the attacker's perspective, we aim to highlight the critical need for robust security measures in mobile applications. However, implementing such security measures can be challenging for developers, often requiring significant time, expertise, and resources. This is where innovative solutions become crucial. Recognizing this gap in mobile app security, Appdome provides comprehensive protection against these threats through zero-code integration, allowing developers to secure their mobile apps effortlessly.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", - "name": "Simona Milanovic" + "id": "36110e7b-c926-48ec-be43-b1a10e0641a0", + "name": "Gil Hartman" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264365, + "name": "Accessibility" }, { - "id": 185713, - "name": "Compose" + "id": 264372, + "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185741, - "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 3 }, { - "id": 36717, - "name": "Hamilton", + "id": 53982, + "name": "Nest", "session": { - "id": "501854", - "title": "Developer Productivity On a Budget", - "description": "Adam and Jordan both work on Platform teams at different companies supporting many distributed teams that contribute to their Android codebases. \r\n\r\nWe'd like to share some of the things we've built for our Engineers to help them become more productive and ensure that they deliver great experiences for our users.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "702107", + "title": "[REDACTED]: How to keep your app's secrets, secret", + "description": "Every app has secrets! These could be in many forms (no judgment here!) but in this talk, we'll focus on the most common use case of storing API keys or data in-app that we'd rather not make readily available to malicious actors.\r\n\r\nIn this talk, we'll look at answering one of the most asked questions in mobile security, \"How do I secure my API keys\" and ensure you have the knowledge and tools you need to do so.\r\n\r\nYou'll leave this talk with:\r\n- Actionable security best practices for securing your app\r\n- An idea of how to store API keys, at scale\r\n- Multiple examples of how to securely access your API keys\r\n- Some fun real-world examples of when things go wrong\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", - "name": "Adam Ahmed" - }, - { - "id": "1849dced-2e7b-4d3a-b8f9-3c6e9a62df29", - "name": "Jordan Terry" + "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", + "name": "Ed Holloway-George" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185724, - "name": "API" + "id": 264369, + "name": "Other" }, { - "id": 185731, - "name": "Tooling" + "id": 264372, + "name": "Security" }, { - "id": 185745, - "name": "CI/CD" + "id": 264384, + "name": "Android" }, { - "id": 185729, + "id": 264378, "name": "Gradle" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 4 }, { - "id": 36718, - "name": "Liskov", + "id": 54703, + "name": "Chromecast", "session": { - "id": "529488", - "title": "A/B Test Best Practices and Fun Case Studies from Deliveroo", - "description": "At Deliveroo we run hundreds of A/B tests per year, and sometimes they return very unexpected results! Over the years, we’ve learned lots about running A/B tests, and today we’ll share some best practices for success. We’ll share some stories about some of the strangest results we’ve seen and by the end, we hope to convince you to start running A/B tests in your apps too! \r\n\r\nTopics we will cover:\r\nWhat is an A/B test?\r\nWhat are some of the most surprising results we have got from A/B tests at Deliveroo?\r\nWhat did we learn from those A/B tests?\r\nWhat are the best practices for running A/B tests?\r\nHow can I convince my boss that we should be running A/B tests at our company?\r\n\r\nCome along to find out if handling process death impacts how often people order food, and exactly how valuable your splash screen is!\r\n", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "757537", + "title": "Roundtable: Mastering Release Management", + "description": "Teams of all sizes require release management processes that should ensure smooth delivery of app updates, while balancing the need for innovation with stability and user experience. Coordinating teams, managing release cycles, and mitigating risks while adapting to the Android ecosystem can be challenging. Join this open roundtable discussion to examine:\r\n•\tWhat are the biggest challenges in managing releases within the Android ecosystem, especially with the variety of devices and OS versions?\r\n•\tHow do you allow frequent releases while minimizing the toil of release management?\r\n•\tHow do you ensure a smooth release process while balancing the need for continuous innovation and feature updates?\r\n•\tWhat are some best practices for testing and quality assurance before rolling out an Android release to a large user base?\r\n•\tHow do you manage the potential impact of an unsuccessful or buggy release, and what steps are taken to mitigate such risks?\r\n•\tWhat role do user feedback and crash analytics play in refining subsequent Android releases?\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "0ef1e55b-29b9-4c4b-938d-c59b6c8ea9d3", - "name": "Jamie Adkins" + "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", + "name": "Maria Neumayer" }, { - "id": "48a744a3-c5e5-40b7-8c64-eb134ff4dee7", - "name": "Edward Harker" + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 5 }, { - "id": 39641, - "name": "Booth", + "id": 54706, + "name": "Ask Android", "session": { - "id": "528793", - "title": "Sync Data with your Mobile Kotlin App The Easy Way!", - "description": "Part of building a great mobile app is syncing data between the app and your back end database. That used to mean creating and testing API calls, worrying about storing local data while offline, performing conflict resolution among multiple users, user authentication and authorization, network communications and error handling, and security. In this talk, I’ll show you how to address all of those issues effortlessly with MongoDB Atlas and Atlas for the Edge. Atlas is the Developer Data Platform from MongoDB – a multi-cloud database-as-a-service offering with state-of-the-art features to accelerate your development projects. Atlas for the Edge brings the power of MongoDB closer to your users at any location. Attendees will see how to build a mobile Kotlin app with the Atlas Edge SDK that easily connects with a MongoDB Atlas on the back end for fast bi-directional sync, out-of-the-box networking and conflict resolution, and built-in offline ACID-compliant data storage on the mobile device.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "764323", + "title": "Ask Android! - Compose, KMP, Gemini in AS (copy)", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "60004028-029a-4865-88eb-acd45debf033", - "name": "Mark Brown" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185744, - "name": "sponsor" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 + "index": 6 + } + ] + }, + { + "slotStart": "12:30:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "46932aef-14c0-4b86-80c3-50646e240b66", + "title": "Lunch", + "description": null, + "startsAt": "2024-10-31T12:30:00", + "endsAt": "2024-10-31T13:40:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 }, { - "id": 39642, - "name": "Lamarr", + "id": 54706, + "name": "Ask Android", "session": { - "id": "528157", - "title": "Offline First Architecture with Bloc and Reactive Database", - "description": "Building apps that work flawlessly with limited network accessibility is not trivial. However, with more and more demanding users it's a necessity. There can be several approaches to offline first architecture and in this talk we'll look into one of them. You'll learn how to use flutter_bloc for handling user's actions and data, how to enable database to host persistent queue of actions, and resolve other challenging topics when the app is offline.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "764331", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T12:30:00", + "endsAt": "2024-10-31T13:40:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "29ba7916-20b4-48e5-9715-bddfbdd1aa3a", - "name": "Dominik Roszkowski" + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "31e8a089-eb2f-437c-ac09-bb55af17f934", + "name": "Donovan McMurray" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "12:05:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "cc54e1b9-b1bc-42ae-9ad7-d2a8e6aa24aa", - "title": "Break", - "description": null, - "startsAt": "2023-10-26T12:05:00", - "endsAt": "2023-10-26T12:20:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 6 } ] }, { - "slotStart": "12:20:00", + "slotStart": "13:40:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "528710", - "title": "Go the extra mile for your media app on Android", - "description": "You have a great application that can read media like a video or audio, that’s great!\r\n\r\n\r\nBut maybe you can go a tiny bit further, to enable your user to have more features, and a more premium experience!\r\n\r\n\r\nA ton of tiny things are easy to do, and can leverage your app to go even further, for instance : \r\n* Downloadable content\r\n* Spatial Audio with Head Tracking\r\n* Enable HDR\r\n* Enable Picture in Picture\r\n* Support HALF_OPENED for Foldables\r\n* Support Android Auto, TV and Wear\r\netc..\r\n\r\nA lot of things to do that do not require a lot of development!\r\n\r\nThe goal of this session is to show you what you can add to your app media experience, and tell you how it’s easy to do!\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "728763", + "title": "Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!", + "description": "Let's have fun with animations, gestures and sensors!\r\n\r\nUsing Compose Multiplatform, we'll go over how to create animations using gestures and sensor events for Android & iOS. We'll cover some basics like how to get the device motion and position information, how to track gestures in the screen, and how you can combine them with animations to have fun! \r\n\r\nAfter this talk, you'll have a better understanding on how to use the sensor frameworks, how to make your own gesture effects, and how to create interesting animations in an easy way.\r\n\r\nKeep it fun, keep it animated!", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f3facba0-c34d-4c87-a7e7-a81a67c8029d", - "name": "Antoine Danois" + "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", + "name": "Nicole Terc" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185709, - "name": "Soft Skills" + "id": 264359, + "name": "Kotlin" }, { - "id": 185713, + "id": 264364, "name": "Compose" }, { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185717, - "name": "Foldables" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185731, - "name": "Tooling" + "id": 264384, + "name": "Android" }, { - "id": 185730, - "name": "Android Studio" + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "530078", - "title": "Think outside the server", - "description": "Securing your server API is a common method to safeguard your Android application, however, it may not always be sufficient. In certain situations, you may need to have additional measures in place to protect the mobile app client beyond just server-side security.\r\n\r\nIn this presentation, we will explore a few of these scenarios and why they are so important to be aware of. In conclusion of this presentation, you will be able to identify, understand, and deal with the common client-side security challenges that you may encounter.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "716104", + "title": "Swift Cheatsheet for Android/Kotlin Developers", + "description": "Knowing common Swift patterns and how they translate to Kotlin can help us understand better what the code does. Whether to see how some feature is implemented on the neighbor platform, perform code reviews, review or write tech specifications/proposals, or work with Kotlin Multiplatform.\r\n\r\nWe will go over some of the basics of the Swift language and how it compares to Kotlin. Additionally, we will cover common patterns that you might find in a typical iOS project like optional bindings, dictionaries, extensions, structures, and protocols.\r\n\r\nLeave this talk confident you can read, understand, and review Swift/iOS code. It will also help you start with Kotlin Multiplatform where knowledge of Swift and SwiftUI is important.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5dd21448-f468-44ad-9220-75d25e8e2370", - "name": "James Hamilton" + "id": "c24d26ff-8a59-4954-b768-fcd09d8bb0a3", + "name": "Domen Lanišnik" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" + "id": 264359, + "name": "Kotlin" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 1 }, { - "id": 36718, - "name": "Liskov", + "id": 49272, + "name": "Things", "session": { - "id": "528347", - "title": "Become a hero to your DevSecOps team", - "description": "Get in front of API Security. Mobile apps are the front line of your company’s customer engagement. With the correct architecture and security, they can also become the first line of defence in protecting your company’s infrastructure. This presentation will take you through a worked example showing how an Android app can strengthen an organisation’s cyber-defense.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "748950", + "title": "How to Optimize, Validate and Deploy ML Models On Device", + "description": "In this workshop we address the common challenges faced by developers migrating AI workloads from the cloud to edge devices. Qualcomm aims to democratize AI at the edge, easing the transition to the edge by supporting familiar frameworks and data types. ​\r\n\r\nWe'll talk through why ML is best done on device and how to easily select a model for your use case, train (or fine-tune), and then compile for the device of your choice.\r\n\r\nWe'll walk through how to get started, iterate on your model and meet performance requirements to deploy on device! We'll show examples on how to optimize models and bundle the model into your application.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", - "name": "Neal Michie" + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264355, + "name": "Workshop" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185735, - "name": "Android" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 2 }, { - "id": 39640, - "name": "Hopper", + "id": 53981, + "name": "Stadia", "session": { - "id": "529968", - "title": "Remote Build Cache - overcoming the network latency tax", - "description": "Lengthy builds and tests are a tax on productivity, job satisfaction and CI infra costs. The solution is to modularise apps into sub-projects and connect build systems to a remote build cache. But in doing so how can we minimise the latency and bottlenecks of retrieving cache entries? This talk will show you how to turbo-boost your build benchmarks with remote caching.\r\n\r\n\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "725157", + "title": "Click-free releases & the making of a CLI app", + "description": "At TravelPerk, we've developed a tool that fully automates our release process, eliminating the need for human intervention. This was achieved using a lightweight Kotlin CLI app. In this talk, we'll provide an in-depth look at the design and implementation of this tool, and guide you through building your own Kotlin CLI app. Join me to explore the power of Kotlin and Gradle in creating robust, automated solutions.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3be14346-8241-4bd9-9a8e-6771276bc270", - "name": "Tamás Bazsonyi" + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264359, + "name": "Kotlin" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264380, + "name": "Tooling" }, { - "id": 185745, + "id": 264394, "name": "CI/CD" }, { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185729, + "id": 264378, "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "529504", - "title": "Supporting your architecture with code coverage and static analysis rules", - "description": "One challenge of engineering teams is how to ensure you're writing high-quality code. Code coverage has traditionally been one measure for this, but aiming for complete coverage across your codebase will rarely lead to meaningful results. \r\n\r\nIn this talk we'll look at how you can use inclusion and exclusion rules within your Android codebase to ensure that any measurements help enforce your architecture patterns, allowing engineers to focus on the code that matters and giving them confidence they're building in the right way.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "756374", + "title": "Tools that could help you to target newer SDK level and improve compatibility", + "description": "It could be a painful journey when developers update the app's target SDK level. There could be new APIs, deprecation of current APIs and gated or non-gated change. \r\n\r\nThis session will share major changes in Android 15, the common mistakes when targeting the latest version of Android, why developers should target the latest version as soon as possible, and what tools are available to help developers.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3e36c7fe-b7ed-4a1a-b197-5937e0c611f6", - "name": "Michael Tweed" + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, + "id": 264363, + "name": "Testing" + }, + { + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185731, - "name": "Tooling" + "id": 264384, + "name": "Android" + }, + { + "id": 264379, + "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", "session": { - "id": "517620", - "title": "Stop Treating Accessibility as an Afterthought: Concrete Steps to Build Inclusive Apps", - "description": "\u2028\u2028In today's digital world, accessibility is more important than ever. However, accessibility is often treated as an afterthought in the development process, which can lead to exclusion and frustration for users with disabilities. This talk aims to change that by discussing how to make accessibility a core part of your development workflow. We will cover topics such as collaboration within your team, available tools and when it makes sense to use them, and modifications to your project to ensure adding accessibility is a pain-free experience for feature development and testing. By implementing these strategies, you can create a more inclusive product and provide a better user experience for all.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "757518", + "title": "Roundtable: EM or IC? What’s Right for Me?", + "description": "Choosing between a career path as an engineering manager or an individual contributor is a pivotal decision for engineers as they advance in their careers. This choice often hinges on personal interests, leadership aspirations, and the desire for hands-on technical work. Let’s get together and discuss:\r\n•\tWhat are the key factors that developers should consider when deciding between becoming an engineering manager or staying on the individual contributor track?\r\n•\tHow can someone assess whether they have the necessary skills and mindset to transition into a management role?\r\n•\tWhat are the most significant challenges and rewards that come with each path—engineering management versus individual contribution?\r\n•\tHow can companies support employees who are undecided or want to explore both career paths simultaneously?\r\n", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8e7913b6-fce4-419a-b862-7a1b535d1af9", - "name": "Manuela Sakura Rommel" + "id": "af079576-c7db-497a-990d-919fc2472e45", + "name": "Anastasia López" + }, + { + "id": "f40ca183-824d-43d9-9e63-ee0edb5aa307", + "name": "Ataul Munim" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764343", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", "categoryItems": [ { - "id": 185714, - "name": "Accessibility" - }, + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185727, - "name": "Flutter" + "id": 306233, + "name": "Office Hours" } ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "12:40:00", + "slotStart": "14:20:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "b6c668c1-a377-4f40-923f-90310a20ba01", - "title": "Lunch", + "id": "2f36980d-7a9d-4899-8e0b-9e1de74805e0", + "title": "Break", "description": null, - "startsAt": "2023-10-26T12:40:00", - "endsAt": "2023-10-26T13:50:00", + "startsAt": "2024-10-31T14:20:00", + "endsAt": "2024-10-31T14:35:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764345", + "title": "Ask Android! - Compose, GenAI, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:20:00", + "endsAt": "2024-10-31T14:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 } ] }, { - "slotStart": "13:50:00", + "slotStart": "14:35:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "538660", - "title": "Easy screenshot testing with Compose", - "description": "UI tests are an integral part of a good testing story, but they tend to be a source of flakes and can cause slow CI runs. With screenshot tests you can make multiple assertions at the same time, prevent regressions across multiple form factors and verify visual appearance of Compose UIs. In this talk we'll walk you through how screenshot testing works, how to manage golden images, how to create a strategy of what to test, which of the available solutions to go for to speed up your tests runs, and also we'll take a peek at the upcoming support in the Android Gradle Plugin and Android Studio.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "734739", + "title": "Video Editing on Android with Media3", + "description": "Learn how to use Media3 Editing libraries to edit, trim, concatenate and apply effects to video frames on Android. \r\nImplementing video editing has always been a challenge for Android developers. Media3 is a jetpack library that is meant to make video editing easy, performant and reliable. \r\nIn this session you will learn about Transformer APIs, how to apply effects and concatenate multiple media files. ", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", - "name": "Jose Alcérreca" + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185712, - "name": "Testing" + "id": 264374, + "name": "API" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264384, + "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "530107", - "title": "Threat Landscapes, Threat Models, and Threat Prevention: A Practical Guide for Android Developers", - "description": "Mobile apps are under constant attack. And the attackers' targets are not always the ones you might expect.\r\n\r\nJoin us for a whirlwind tour of the Android threat landscape, and discover some practical insights into what you can do to to fortify your apps:\r\n\r\n- Watch some real-time exploits of a demo banking app and start thinking more like an attacker\r\n- Get some tips on creating a threat model for your apps\r\n- Explore the protection measures that can help your apps to defend themselves", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "732898", + "title": "Your app could use a secondary screen!", + "description": "Learn how an Android application could be used in a desktop-style environment while leveraging a secondary screen. We'll go through some key technical aspects to consider while developing an enterprise-oriented application and how Zebra is helping the developers go beyond what Android already offers.\r\n\r\nKey takeaways:\r\n- How an Activity is getting Multi Window support\r\n- Lifecycle impacts while using the application on 2 screens\r\n- Working with DisplayManager APIs to launch your activity on a secondary screen", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e98ce7fa-cb48-4eb6-8a60-be9d4b4c0b6a", - "name": "Ivan Kinash" - }, - { - "id": "e99fa2a9-4e01-4534-907d-00dbb1447360", - "name": "Ben Thompson" + "id": "de4da391-af75-405b-8816-186c5d4e8fde", + "name": "Daniel Neamtu" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 1 + }, + { + "id": 49272, + "name": "Things", + "session": { + "id": "768482", + "title": "How to Optimize, Validate and Deploy ML Models On Device (Part II)", + "description": "We'll walk through the steps to bring your ML model on device. In this hands on section of the workshop we will demonstrate the end to end workflow for a sample use case, using Qualcomm AI Hub to optimize a model and deploy it on device. \r\n\r\nWe'll then help you get set up and walk through various examples on how to use Qualcomm AI Hub. The Qualcomm AI Hub team will be there to teach you the ins and outs, enabling you to use the platform and bring your ML use case on device quickly and easily.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ { - "id": 185722, - "name": "Security" - }, + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185735, - "name": "Android" - }, + "id": 264355, + "name": "Workshop" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ { - "id": 185744, - "name": "sponsor" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 2 }, { - "id": 36718, - "name": "Liskov", + "id": 53981, + "name": "Stadia", "session": { - "id": "495541", - "title": "Composing ViewModels: Breaking ViewModels into smaller self-contained UI models", - "description": "In early times of Android development we used to have big activities. All view and business logic were being written in activities. Then fragments were introduced so that activities started to become leaner. Then several architectural patterns have started to appear in the Android scene: MVP, MVVM, MVI, etc. While all these were happening, ViewModel like classes started to get bigger and bigger. They have become the new big activities. However, we haven't often considered splitting view models into manageable self-contained granular ones.\r\n\r\nViews are composable and they can be composed to build bigger views. What about introducing smaller ViewModels, namely UI models, and using them to build composable UI models/View Models? \r\n\r\nIn this talk, we will introduce the micro-feature architecture that leverages granular UI models. We'll demonstrate how this approach enables us to create leaner UI hosts by breaking down view models into smaller, self-contained logical UI models. Moreover, we'll explore the distinctions between a UI model and a Jetpack ViewModel. \r\n\r\nWe'll delve into the advantages offered by micro-feature architecture, particularly in terms of single responsibility, composability, reusability, and testability. \r\n\r\nBy the end of the talk, you'll have a comprehensive understanding of how adopting this architecture can significantly enhance your development process and empower you to build high-quality, scalable applications which has highly dynamic contextual screens. \r\n\r\nMicro-feature architecture is used in several screens of 2 big apps in production which are now being used by 10M+ users. We will be also sharing our learnings about using this approach in these apps. ", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "734309", + "title": "Crafting Narratives: Shaping TalkBack with Compose Semantics", + "description": "\r\nThe focus of my talk will be on making modern, complex designs more intuitive for screen reader users using an empathetic approach . \u2028We will see how to create thoughtful and user-friendly narratives for accessibility by using Jetpack Compose semantics and optimizing the TalkBack experience.\r\n\r\nKey Takeaways:\r\n1. Creating Effective Narratives: Tips for simplifying complex UI elements.\r\n2. Testing Accessibility: Methods for validating and improving semantic changes.\r\n3. Designing for Complexity: Making complex UIs intuitive and accessible.\r\n4. Using Semantics Wisely: Enhancing accessibility with thoughtful design.\r\n", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5c3850ee-778a-4904-bd52-a3927ec54dc6", - "name": "Hakan Bagci" + "id": "bcf3c75a-e105-41de-8891-53f6b673e58a", + "name": "Sachin Sapkale" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185713, - "name": "Compose" - }, - { - "id": 185716, - "name": "Modularization" + "id": 264363, + "name": "Testing" }, { - "id": 185735, - "name": "Android" + "id": 264365, + "name": "Accessibility" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "535307", - "title": "What's new in Paparazzi?", - "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this talk, John will give an overview of how it works, followed by an update on what the Cash App Android team has been working on since then, including:\r\n\r\n* Resource and asset loading improvements\r\n* Video snapshot support\r\n* Snapshots from @Previews\r\n* Compose Multiplatform support\r\n\r\n...and more!\r\n\r\nBackground:\r\n\r\nPaparazzi is an Android testing library that allows you to render your application screens without a physical device or emulator!\r\n\r\nIt has been immensely improving the Android UI testing loop on Cash App and other well-known apps since 2019 by allowing you to refactor your screens with confidence while running blazingly fast on the JVM versus navigating on slow devices.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "736362", + "title": "Streamlining Android app development and release processes", + "description": "Building, testing and releasing Apps for Android is unique with it's own challenges.\r\n\r\nDuring this session we’ll walk through three ways to make building, testing and releasing apps faster and easier for your whole team. Specifically, we’ll look at these three key points:\r\n\r\nAutomating testing and debugging by using CI/CD best practices and out of the box integrations.\r\n\r\nDelivering faster green builds with Bitrise Build Cache. We’ll look at some benchmarks and some real world examples of optimizing the execution.\r\n\r\nFrom beta distribution to production rollout, what steps are there and how these can be optimised.\r\n\r\nBy the end of this talk you'll have an idea on how to take the next steps on your CI/CD journey.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", - "name": "John Rodriguez" + "id": "3be14346-8241-4bd9-9a8e-6771276bc270", + "name": "Tamás Bazsonyi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264380, + "name": "Tooling" }, { - "id": 185712, - "name": "Testing" + "id": 264394, + "name": "CI/CD" }, { - "id": 185714, - "name": "Accessibility" + "id": 264384, + "name": "Android" }, { - "id": 185730, - "name": "Android Studio" + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", + "session": { + "id": "606a45c5-6e96-49d9-b146-808283235582", + "title": ".", + "description": null, + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", "session": { - "id": "517499", - "title": "Exploring Records and Patterns", - "description": "Dart 3 is packed with new experimental language features, including Records, Pattern Matching, Algebraic datatypes, and Destructuring. Like Extensions, they’ll open up new opportunities for packages and ways to express oneself in code. Through concrete examples, Pascal will guide you through the syntax of these new features and demonstrate how they can be used to create more expressive and maintainable code.\r\n\r\nYou’ll learn about:\r\n\r\n- The benefits of the new Dart 3 language features\r\n- The considerations in designing these features\r\n- What the syntax looks like in isolation\r\n- New patterns and anti-patterns and how they will shape our Dart code of tomorrow\r\n\r\nWhether you're a seasoned Dart developer or just getting started, this talk will help you get up to speed on the latest advancements in the language. Don't wait for the future to arrive – start exploring these new features today and take your code to the next level.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "764347", + "title": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f117aa78-7e11-451d-b7ba-fb19ac866f31", - "name": "Pascal Welsch" + "id": "81ee4554-4839-4192-9a74-ecb32dff2819", + "name": "Alex Vanyo" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "14:30:00", + "slotStart": "14:55:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "14331e69-603a-42c2-83cc-dee009c659b3", + "id": "b28b4077-1368-4b9a-9ea2-ebd718631283", "title": "Break", "description": null, - "startsAt": "2023-10-26T14:30:00", - "endsAt": "2023-10-26T14:45:00", + "startsAt": "2024-10-31T14:55:00", + "endsAt": "2024-10-31T15:10:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764350", + "title": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:55:00", + "endsAt": "2024-10-31T15:10:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "327f56b4-c631-4489-9590-37bdfc856e3a", + "name": "Miguel Montemayor" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 } ] }, { - "slotStart": "14:45:00", + "slotStart": "15:10:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "529442", - "title": "Advanced Android Canvas APIs", - "description": "Dive into the Android Canvas APIs, the underlaying Skia engine and how it powers both native Android and Jetpack Compose UIs. This talk offers a deep dive into drawing primitives, transformations, and custom views, showcasing how to craft dynamic and visually engaging user interfaces. Join me to uncover the secrets of advanced Canvas techniques that will elevate your app's visual appeal and user experience.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "710572", + "title": "Rethinking Home - How testing techniques and code design reshaped the new Spotify Home feature", + "description": "Let's dive into the dynamic world of Spotify Home development, where scalability and excellency are key. In this talk we will learn about the relationship between good code design and testability. How these principles synergise to influence the new shape of core Home features. Moreover we will go through the value of different test types and learn insights on how to make code more testable as well as actionable strategies to enhance code design in general ", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "71373c0f-53e8-4020-8d16-1bdfe603c3c7", - "name": "Markus Hintersteiner" + "id": "6ee5ee5a-1bdd-4c54-8178-fad51f86a78a", + "name": "Daniel Horowitz" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185735, - "name": "Android" + "id": 264363, + "name": "Testing" }, { - "id": 185741, - "name": "UI/UX" + "id": 264368, + "name": "Techniques & Guides" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "530222", - "title": "App architecture - singular vs plural", - "description": "A single architecture is usually at the core of our apps. We build our app and its features around it and by its rules. And while our chosen architectural pattern might work, and even work well, do we really benefit from a \"one per app\" approach? \r\nIn this talk, we'll do a quick run-through of the most popular architecture patterns in Android and try to figure out if it's better to strive for a singular architecture per app or if we should consider different ones for different features.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "751161", + "title": "Designing scalable Compose APIs", + "description": "As more and more apps and teams migrate to Compose, it's important to establish clear guidelines for writing high-quality Compose code. This talk will cover best practices and guidelines for developing idiomatic Compose APIs, with topics such as how to think about and plan for your components, how to leverage Kotlin and naming conventions, and define a solid structure of your component, and finally how to verify and maintain these APIs. We'll discuss the rationale behind these guidelines and how they can help developers write code that is more scalable, performant, and consistent.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "163e9ce7-9042-4482-8f9e-f713d92ceae4", - "name": "Yoni Tietz" + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264359, + "name": "Kotlin" }, { - "id": 185719, - "name": "Other" + "id": 264364, + "name": "Compose" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 1 }, { - "id": 36718, - "name": "Liskov", + "id": 49272, + "name": "Things", "session": { - "id": "502453", - "title": "I have a Date", - "description": "One common task in Android app development is working with Date object, and Kotlin provides several options for managing it. \r\n\r\nIn this talk, we will explore the different ways to represent and manipulate dates in Kotlin, including using the built-in java.util.Date class, the third-party Joda-Time library, and the java.time package introduced in Java 8. \r\n\r\nWe will also discuss best practices for formatting and displaying dates to the user, and handling time zones and daylight saving time. By the end of this talk, attendees will have a strong understanding of how to effectively manage dates in their Kotlin Android apps.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "745796", + "title": "Securing Android: Tackling Advanced Threats and Enhancing App Security", + "description": "What threats are Android apps dealing with these days? In this talk, we will look at the latest security challenges and the best ways to keep your apps safe from new threats.\r\nWhen companies rush to release apps, they often skip important security steps, which can lead to higher costs and more risks. It's important to build security into your app from the beginning. We will discuss common risks for Android apps, how to handle them, and the challenges developers and security experts face when trying to protect code, keys, and data.\r\n\r\nKey Takeaways:\r\n\r\n* Get up to speed on the latest security threats specifically targeting Android apps.\r\n* Explore the latest advanced rooting technique such as Magisk and app instrumentation and the best way to protect your app.\r\n* Explore the differences between shrinking/minification and advanced techniques for obfuscation and anti-tampering, including cryptographic key protection.\r\n* Discover how to embed security throughout the development process to minimise risks and build resilient apps.\r\n\r\n\r\n", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "4674e36f-50f8-4ffb-a486-679d730c759e", - "name": "Renaud Mathieu" + "id": "24eb82e3-3ea7-4b45-9ffb-d63cac580e88", + "name": "Mohamed Kerroumi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264372, + "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 2 }, { - "id": 39640, - "name": "Hopper", + "id": 53981, + "name": "Stadia", "session": { - "id": "527630", - "title": "White-box Cryptography", - "description": "Dive into the world of white-box cryptography and discover its superior data protection compared to traditional methods. \r\n\r\nLearn how it guards against a range of threats including reverse engineering, and other common threats to applications. \r\n\r\nJoin us to elevate your security expertise and stay ahead in the ever-evolving landscape of data protection.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "757989", + "title": "Rethinking Apps with Local-First Architecture", + "description": "Modern apps are expected to function flawlessly even in low-connectivity edge environments, but traditional centralized networking approaches often fall short. In this session, we’ll explore how we got here and where we’re headed, diving into the unique challenges of local-first architecture. We’ll look at some of the tough problems in this space and examine solutions that open new possibilities. By rethinking these aspects in your app, you can push the boundaries of what your app can do and create experiences that will amaze your users.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c746f9bf-2963-4549-b2f8-26cdafa24936", - "name": "Oliver Williams" + "id": "da380fb8-c8ba-47e9-b224-26647296cff5", + "name": "Richard Das" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185731, - "name": "Tooling" + "id": 264369, + "name": "Other" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264381, + "name": "Cross-Platform" }, { - "id": 185739, + "id": 264388, "name": "Libraries" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "540429", - "title": "REST in Peace: A Journey Through API Protection", - "description": "Isn't Droidcon a Mobile Developer's conference? So, why would I care about protecting REST APIs? Well, think twice! API protection starts in the app, as the protection level of the API will be only as strong as the protection mechanisms built in the app. Join us for this entertaining talk were we will cover the typical mechanisms used to protect REST APIs and how they can be \"exposed\" if insufficient protection is put into the application. And, of course, guide you into putting the right measures inside the app so that both, app and backend, are sufficiently protected.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "709487", + "title": "Dynamic Flow, the Wise approach to server driven UI", + "description": "In this presentation I will talk about how we combined the expertise of Android, iOS and Web engineers to create a server driven UI solution using Kotlin Multi Platform (KMP). This approach has allowed Wise to efficiently scale our product and business, resulting in more than 500 unique native screens being generated by the backend. \r\n\r\nLearn how this project has empowered backend engineers, streamlined UI development process and enhanced cross-platform collaboration.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "377a3c7a-eba5-48e0-9057-0a5ff91061cc", - "name": "Marc Obrador Sureda" - }, - { - "id": "c6100c4b-13c4-433e-8dcd-ea0c92939179", - "name": "Andreas Luca" + "id": "f5e37102-a8be-4e34-be47-b98329cd2a23", + "name": "Phellipe Silva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264361, + "name": "KMP" }, { - "id": 185724, - "name": "API" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185744, - "name": "sponsor" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", "session": { - "id": "529978", - "title": "Building RTL-Ready Flutter Apps", - "description": "Are you looking to expand your app's reach to the global market and support RTL languages? Building RTL apps can be complex, but it's essential to ensure a seamless experience for millions of Arabic, Hebrew, and Persian-speaking users.\r\n\r\nThis talk will cover Flutter's RTL implementation and internationalization support. You'll learn about important widgets like Directionality, TextDirection, MediaQuery, and EdgeInsets that are crucial for creating RTL-friendly user interfaces.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "768515", + "title": "Kotlin by JetBrains, present and future", + "description": "Have a chat with Developer Advocates from JetBrains about anything Kotlin related on your mind. Language evolution, tooling, development practices, Kotlin Multiplatform and Compose Multiplatform... We're here for all of it!", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "cae62422-4040-4130-b345-29c8cd65f41b", - "name": "Raouf Rahiche" + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" + }, + { + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 5 - } - ] - }, - { - "slotStart": "15:05:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "882a3337-793f-44e5-a298-ddf8cf6437a6", - "title": "Break", - "description": null, - "startsAt": "2023-10-26T15:05:00", - "endsAt": "2023-10-26T15:20:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": null - }, - "index": 0 - } - ] - }, - { - "slotStart": "15:20:00", - "rooms": [ + }, { - "id": 36716, - "name": "Lovelace", + "id": 54706, + "name": "Ask Android", "session": { - "id": "508933", - "title": "Meet Jewel: create IDE plugins in Compose ✨", - "description": "Jetpack Compose is the declarative UI toolkit for Android that makes it easy to create beautiful, responsive apps. However, until recently, there was no easy way to use Compose to create IDE plugins without too many compromises. Jewel is a new library that solves this problem by providing a set of tools and components that make it easy to create Compose for Desktop-based plugins for Android Studio and IntelliJ IDEA.\r\n\r\nIn this talk, we will introduce Jewel and show you how to use it to create your own IDE plugins. The session will cover the following topics:\r\n\r\n* Why Compose makes a huge difference in IDE plugin development\r\n* How to set up Compose in your IDE plugin project\r\n* What's Jewel, a library implementing the IntelliJ themes and a composables\r\n* What are the tradeoffs in using Compose and Jewel\r\n* Case studies of existing implementations\r\n* What's the future for Jewel and Compose as a plugin UI framework\r\n\r\nThis talk is intended for Android developers who are familiar with Compose but have never created an IDE plugin because they don't wanna learn Swing. By the end of this talk, you will have a good understanding of how to use Jewel to create your own Compose-based IDE plugins.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "764353", + "title": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", - "name": "Sebastiano Poggi" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" }, { - "id": "eb4483f1-306b-447d-9888-648ec0fc0018", - "name": "Chris Sinco" + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185730, - "name": "Android Studio" - }, - { - "id": 185741, - "name": "UI/UX" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 + } + ] + }, + { + "slotStart": "15:50:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "5264a86b-3452-40bf-b481-44b538e30c10", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T15:50:00", + "endsAt": "2024-10-31T16:05:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 54706, + "name": "Ask Android", "session": { - "id": "524023", - "title": "Working with custom Android devices", - "description": "Android is now well established outside the world of smartphones. Although usually not officially supported, you can find Android running on all kinds of devices, like home appliances, digital signage, and point-of-sales terminals. Being Android developer today means you are likely to encounter these custom devices in your career.\r\n\r\nIn this session you will learn the most important parts of working on these devices. We will cover things like developers boards, board support packages, AOSP builds, firmware updates, and much more.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "764351", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:50:00", + "endsAt": "2024-10-31T16:05:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", - "name": "Erik Hellman" + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "d713390d-9628-412f-9778-9c45aeb6b13e", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 - }, + "index": 6 + } + ] + }, + { + "slotStart": "16:05:00", + "rooms": [ { - "id": 36718, - "name": "Liskov", + "id": 49270, + "name": "Hangouts", "session": { - "id": "528396", - "title": "Android App Performance in a Nutshell", - "description": "This session will give you a head start with actionable advice on the journey to Inspect, Improve and Monitor your app's performance with the Android Performance development process. You'll learn the latest in Baseline Profiles, Perfetto Tracing and Benchmarking.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "714221", + "title": "Jetpack Compose: Drawing without pain and recomposition", + "description": "This is a talk on recomposition in Jetpack Compose and the myths of too many calls it is followed by. I'll briefly explain the reasons behind recompositions and why they are not as problematic as they may seem. I have prepared numerous examples that illustrate how to minimize its occurrence.\r\n\r\nI'll share real-life situations we encountered during the redesign of our main screen. I'll delve step-by-step into how I optimized a particle animation without additional memory allocation and how we successfully reduced the number of recompositions on the screen. My practical guide on parameter tuning will be a great takeaway.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e869461e-b6fb-43e5-a89c-033da87008c0", - "name": "Ben Weiss" + "id": "34b0d49b-00dd-4dc1-ac1c-6de040e8a5b9", + "name": "Vitalii Markus" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, - "name": "Testing" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185735, - "name": "Android" + "id": 264359, + "name": "Kotlin" }, { - "id": 185729, - "name": "Gradle" + "id": 264364, + "name": "Compose" }, { - "id": 185730, - "name": "Android Studio" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 0 }, { - "id": 39640, - "name": "Hopper", + "id": 49271, + "name": "Glass", "session": { - "id": "495134", - "title": "How to stop the ‘Gradle Snatchers’: Securing your builds from baddies", - "description": "Following on from one of the first recorded supply chain attacks against Gradle, this talk will discuss the security concerns surrounding our favorite build tool and how we can protect against them. This starts with gaining an understanding of some of Gradle's common vulnerabilities and how to avoid these within our Android projects. You'll leave this talk with:\r\n\r\n- Insights on the Gradle Wrapper supply-chain attack and how to protect against it.\r\n- An overview of a Gradle dependency attack and how to protect against them.\r\n- A concrete list of security setting best practices within Gradle, including wrapper verification, repository filtering, dependency verification and others.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "708032", + "title": "DIY Server-Driven UI: Building Auto Trader's SDUI platform", + "description": "Join us as we take you through Auto Trader’s 4-year journey in crafting our hybrid Server-Driven UI framework (Composable) and the architecture that powers our mobile apps. We’ll delve into why we decided to build instead of buy, our choice of Kotlin and the designs that fuelled our success. From pitfalls to breakthroughs, this talk will be full of useful insights for anyone considering building SDUI themselves.\r\n\r\nYou should leave this talk with the following:\r\n- Insights into our approach to SDUI\r\n- Building with a small team without stopping feature work.\r\n- Kotlin and Android tech insights of the system.\r\n- How we're leveraging KMP to improve client logic.\r\n- 4 years of learnings, did we make the right choice?\r\n- Tips and tricks for anyone considering the same.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", - "name": "Ed Holloway-George" + "id": "18ce6325-d3f4-4478-99d0-5683c4b924c2", + "name": "Jimmy Ray" + }, + { + "id": "0233c2ee-3bcd-4dee-9de5-c765b8c27cc0", + "name": "Harriet Taylor" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264359, + "name": "Kotlin" }, { - "id": 185735, - "name": "Android" + "id": 264361, + "name": "KMP" }, { - "id": 185729, - "name": "Gradle" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 1 }, { - "id": 39641, - "name": "Booth", + "id": 49272, + "name": "Things", "session": { - "id": "510229", - "title": "Conducting tech interviews", - "description": "Conducting proper tech interviews are hard. It has taken me more than 20 years of interviews, both as a candidate and as an interviewer, to come up with a process and key aspects that I try hard to go through before hosting an interview. \r\nI've also learned as a candidate signs of an unhealthy company and a few take aways that I can apply to when I'm interviewing myself. Both positive and negative.\r\nIn this session I will share my history of technical interviews, what I look for when interviewing, how I want to set the scene, and how I try to always get the best out of each interview. Hopefully you can take away some learnings and inspiration for your own interviews after hearing me out. ", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "736123", + "title": "How we build Spotify for Android Automotive", + "description": "As the automotive industry embraces newer technologies and more mainstream software tech stacks, the in-car entertainment experience is rapidly evolving into a living room on wheels. In this session, we will take you on the journey of bringing Spotify to Android Automotive OS.\r\n\r\nIn this session, we will cover:\r\n\r\n* What is Android Automotive OS? Discover which car manufacturers and models are currently supported, and the different flavours of Android Automotive around!\r\n* Learn about the specific development challenges we faced while building Spotify for Android Automotive, including using Media Center templates to build features, adapting to diverse hardware, and challenging emulator setups.\r\n* Get insights into the unique aspects of releasing an app in the automotive ecosystem, from testing to ensure compliance with automotive standards to coordinating with car manufacturers for software updates.\r\n\r\nPlease join us to discover how we’re making the Spotify experience accessible, enjoyable and safe for drivers and passengers, and the innovative solutions we’ve developed to overcome the challenges of this cutting-edge platform. \r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bbcf99f4-d0a2-4081-afe8-114e09f54a86", - "name": "Bob Dahlberg" + "id": "9c8384de-140e-47e4-aeb1-c85faf090fc8", + "name": "Danielle Vass" + }, + { + "id": "0aef86d4-a69b-4a82-bd8b-5ee05a3e421d", + "name": "Walid Tazout" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 + "index": 2 }, { - "id": 39642, - "name": "Lamarr", + "id": 53981, + "name": "Stadia", "session": { - "id": "530253", - "title": "Google Home, But Better: Smart Home Display with Flutter", - "description": "We may not have realized it yet, but Flutter could become the next big player on embedded devices.\r\n\r\nWe'll take a dive into running Flutter on embedded Devices and build our own Smart Home Display using Flutter, a \r\nRaspberry Pi and embedded Linux.\r\n\r\nWhat's currently supported and what custom Embedder can we use to achieve this? How do we communicate between Flutter and the embedded hardware? How can we integrate and use Flutter in the Maker-Community to open up to the world of IoT?\r\n\r\nThis talk aims to show the potential of Flutter on embedded devices, to give a practical guide on how to start developing Flutter with a Raspberry Pi on embedded Linux, and to showcase a project where Flutter connects to the Internet of Things.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "732956", + "title": "Rewind and Resolve: A deep dive into building Session Replay for Android", + "description": "Understanding and debugging production issues can feel like solving a puzzle with missing pieces. We will dive deep into the journey of building a session replay feature for the Sentry Android SDK that helps developers capture those elusive bugs, respects user privacy, and maintains low overhead.\r\n\r\nIn this talk, we'll explore the technical aspects, including:\r\n\r\n- Taking screenshots of a Window\r\n- Redacting the screenshots to ensure no sensitive user data is leaked\r\n- Encoding the screenshots into a video\r\n- Collecting supporting data like breadcrumbs, network requests, logs, touches to improve debuggability\r\n- Maintaining low performance overhead while implementing all of the above.\r\n\r\nWe'll also demonstrate how our tool empowers Android engineers to gain actionable insights when solving errors, crashes, and ANRs.\r\n\r\nWhether you're an Android developer looking to improve your debugging toolkit or someone interested in the technical guts of the Android OS, the session will cover it all. Join us for a glimpse into the future of mobile debugging – it's a replay you won't want to miss!", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e6858863-4e16-44ab-b2e0-22c940533654", - "name": "Moritz Theis" + "id": "da013a11-7cb0-4b0d-a37e-e29ce98688e0", + "name": "Roman Zavarnitsyn" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185720, - "name": "IoT" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185727, - "name": "Flutter" + "id": 264380, + "name": "Tooling" }, { - "id": 185732, - "name": "Cross-Platform" - } - ], - "sort": 4 - } - ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "16:00:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "41914d30-f362-4008-9408-aa6c1e5a53a6", - "title": "Break", - "description": null, - "startsAt": "2023-10-26T16:00:00", - "endsAt": "2023-10-26T16:15:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": null - }, - "index": 0 - } - ] - }, - { - "slotStart": "16:15:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "516527", - "title": "The terminal is your friend", - "description": "Command line skills are something we sometimes overlook as Mobile Engineers. Fancy tools and IDEs have made our lives very comfortable. We don’t need to spend a significant portion of our time writing commands in our systems like previous generations of Software Engineers and System Administrators.\r\n\r\nYet there are times when either the IDE has decided to go nuts or your version control tool is not syncing properly and you want to go one level down to see what is going on. Or maybe you just simply want to develop your CI skills which are heavy script and command-line based. \r\n\r\nIn this talk, I want to share some tips and tricks to help you level up your command-line skills and make the console a companion for your daily Android work.\r\n", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ - { - "id": "95a6035d-1167-4cc1-9974-bc065a077893", - "name": "Enrique Ramírez" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185699, - "name": "Introductory and overview" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185703, - "name": "Lightning talk" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ + "id": 264384, + "name": "Android" + }, { - "id": 185731, - "name": "Tooling" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 3 }, { - "id": 36717, - "name": "Hamilton", + "id": 53982, + "name": "Nest", "session": { - "id": "516305", - "title": "Designing for Errors in a Kotlin-first SDK: When to throw, nullify, or return a Result.", - "description": "Error propagation is an important consideration for building feature-rich API architecture in Kotlin. Whereas apps may have the flexibility to add external dependencies and laser-focus on specific use-cases, designing an API requires minimal dependencies and wide applicability. This lightning talk is a crash course in Kotlin error propagation strategies and a case study in our experience designing errors when migrating our SDK from Java to Kotlin. We will go over the various techniques that Kotlin natively provides for us, including throwing exceptions, returning null, sealed classes, and Result, and how we decided which technique to use for our functions.", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "736493", + "title": "Asynchrony and the infinite conveyor belt: Advanced coroutines and flows", + "description": "Most likely, you already know how to use coroutines and flows. They're part of our everyday existence. But all too often we learn them through repetition and rote learning, rather than forging a meaningful understanding of what's happening deep down. \r\nSo:\r\n\r\n- What *really* happens when you mark a function as 'suspend'?\r\n- How come crazy things like infinite loops on the main thread are possible?\r\n- What's the magic link between coroutines and flows?\r\n- What, really, is a scope? A context? A job?\r\n\r\nAnd there's a satisfying conclusion, too! We find that everything is linked: the path that seems to add complexity, in fact takes us back full circle.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5b17491b-317a-4fec-8b63-3264bc0a9cb9", - "name": "Hudson Miears" + "id": "1fb7ac5d-d848-4d40-840e-d8ae38128ab4", + "name": "Tom Colvin" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264362, + "name": "Flow" }, { - "id": 185724, - "name": "API" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" }, { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 4 }, { - "id": 36718, - "name": "Liskov", + "id": 54703, + "name": "Chromecast", "session": { - "id": "530221", - "title": "Structuring modules in an Android app project", - "description": "Let us suppose we have a multi-module project that builds faster than a monolith. \r\nWe have embraced separation of concerns and can now test a self-contained module without mocking the universe. \r\nWe are impressed with modularization, so we create more modules. \r\nThe question now is: How do we organize these modules?\r\n\r\nIn this session, we’ll dive into how many modules are too many in an Android app project and whether there can be too many. You’ll leave this talk with an understanding of how we can improve Android app modularisation to increase developer productivity, organize a clear project structure, access modules faster and assign ownership for better workflows. ", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "757517", + "title": "Roundtable: Community-Driven Development: How to Contribute to OSS", + "description": "Contributing to and maintaining open-source libraries in Android is a rewarding but challenging endeavor that benefits the developer community while enhancing personal skills. It involves code contributions, issue resolution, and ensuring the library remains stable and relevant over time. Join us as we have an open discussion about:\r\n•\tWhat motivated you to start contributing to or maintaining an open-source library in the Android ecosystem, or how do you get started?\r\n•\tWhat are the biggest challenges you face when maintaining an open-source project, particularly when managing contributions from other developers?\r\n•\tHow do you ensure that an open-source library remains compatible and up-to-date with new Android releases and API changes?\r\n•\tWhat are some best practices for encouraging meaningful contributions and fostering an active community around your open-source project?\r\n•\tHow do you balance the time commitment of maintaining an open-source library with other professional or personal responsibilities?\r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c35f2359-2c5c-4b88-a4bc-024a6c025ce4", - "name": "Kinnera Priya Putti" + "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", + "name": "Nicola Corti" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 5 }, { - "id": 39642, - "name": "Lamarr", + "id": 54706, + "name": "Ask Android", "session": { - "id": "512823", - "title": "Latest State of Flutter Feature for Developing Multiple-Entry Android Application", - "description": "Exploration of latest state of the functionality in Flutter for developing multiple entry Android application. The feature allows developers to build single application with multiple entry points to gate/isolate specific features under exact entry, what can be useful for specific cases, like building POS-terminal app, where custom launcher is used for opening specific features of the app.\r\n\r\nKey takeaways:\r\n- From experiment to release\r\n- When and why to build Android version of Flutter application with multiple entry points.\r\n- How to make it work on Android\r\n- Potential limitations of this feature", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "764365", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "606a1cb6-edd2-42bd-8bd4-08a51ef41c7f", - "name": "Vadym Pinchuk" + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "16:35:00", + "slotStart": "16:45:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "ddd494e2-fd61-411c-89b1-c4d80ba80f36", + "id": "afa619be-3074-4ba1-8e3b-e61c3475d25a", "title": "Break", "description": null, - "startsAt": "2023-10-26T16:35:00", - "endsAt": "2023-10-26T16:50:00", + "startsAt": "2024-10-31T16:45:00", + "endsAt": "2024-10-31T17:00:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 - } - ] - }, - { - "slotStart": "16:50:00", - "rooms": [ + }, { - "id": 36716, - "name": "Lovelace", + "id": 54706, + "name": "Ask Android", "session": { - "id": "538657", - "title": "Enhancing Jetpack Compose App Performance: Tools, Strategies and Optimizations", - "description": "Explore techniques to elevate Jetpack Compose app performance through measurement, debugging, and strategic optimization. Harness the capabilities of sophisticated tools, including Macrobenchmark and Android Studio's profiling suite, to pinpoint bottlenecks. Delve into the implementation and debugging of Baseline Profiles, which mitigate the impact of JIT on your code execution. Additionally, learn strategies to alleviate heavy UI rendering, ensuring a smoother user experience. Join us to gain actionable insights and practical skills for crafting high-performing Jetpack Compose apps that deliver seamless user experiences.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "764370", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:45:00", + "endsAt": "2024-10-31T17:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c95ac59a-7c1e-45b6-ba71-cf6ed861ac4a", - "name": "Tomáš Mlynarič" + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 - }, + "index": 6 + } + ] + }, + { + "slotStart": "17:00:00", + "rooms": [ { - "id": 36717, - "name": "Hamilton", + "id": 49270, + "name": "Hangouts", "session": { - "id": "530024", - "title": "Put Your Tests on a Diet: Testing the Behavior and Not the Implementation", - "description": "How do you write tests? How much time do you spend writing tests? And how much time do you spend fixing them when refactoring?\r\n\r\nA few short years ago, we would test a class in JUnit by stacking one test after the other, mocking each one of its dependencies. But for large apps, writing tests in this way without any consideration of architecture can easily become a bottleneck.\r\n\r\nFor a task that takes so much of our time every day as developers, there is surprisingly little discussion online about optimal test design. At Perry Street Software, publisher of two of the world's most popular LGBTQ+ dating apps, after struggling writing and maintaining our unit tests in the past, we have spent much time developing what we like to call the Unit Testing Diet.\r\n\r\nOur Unit Testing Diet provides a healthy way to test and refactor an app at scale, by testing the behavior that a user performs and the outcome that they will perceive, without testing implementation details.\r\n\r\nJoin this talk to learn how to:\r\n\r\n1. Test your ViewModels, UseCases, and Repositories in a Given-When-Then style, without using mocks\r\n2. Test the behavior and not the implementation details\r\n3. Refactor your code without breaking your tests", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "771426", + "title": "#TheAndroidShow - Leadership Keynote", + "description": "Join members of the Android leadership team, including Matthew McCullough, Tor Norbye and Clara Bayarri for insights into what's new in Android", + "startsAt": "2024-10-31T17:00:00", + "endsAt": "2024-10-31T17:20:00", "isServiceSession": false, - "isPlenumSession": false, + "isPlenumSession": true, "speakers": [ { - "id": "85d2aab0-cf59-492c-83fb-ff0a8490fb85", - "name": "Stelios Frantzeskakis" + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264357, + "name": "Keynote" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 0 + } + ] + }, + { + "slotStart": "17:20:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "103ac95c-d0ef-4dad-aeed-14e8261c947f", + "title": "Break", + "description": null, + "startsAt": "2024-10-31T17:20:00", + "endsAt": "2024-10-31T17:30:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false }, - "index": 1 - }, + "index": 0 + } + ] + }, + { + "slotStart": "17:30:00", + "rooms": [ { - "id": 36718, - "name": "Liskov", + "id": 49270, + "name": "Hangouts", "session": { - "id": "517414", - "title": "And Gradle says: sharing is caring - Or why Gradle Plugins are all you need for your Configuration", - "description": "Have you ever been in dependency hell? Are you tired of copying and pasting your setup from one project to another? Do you wish there would be an easy way to share your configurations, workflows, dependencies? Say no more!\r\n\r\nBorn out of painful lessons, this talk will give you a crash course in how you can ship your setup easily to different projects by using the power of Gradle (Convention) Plugins, VersionCatalogs, etc. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "771431", + "title": "#TheAndroidShow - Panel Discussion", + "description": "Join moderator Florina Muntenescu and members of the Android Developer Relations team as they field questions on all the latest in Android development.", + "startsAt": "2024-10-31T17:30:00", + "endsAt": "2024-10-31T18:15:00", "isServiceSession": false, - "isPlenumSession": false, + "isPlenumSession": true, "speakers": [ { - "id": "24ce44ea-d436-4476-88ac-08695de0acfa", - "name": "Matthias Geisler" + "id": "752bc716-f1b8-45d4-b0a5-cfbc3402b68b", + "name": "Florina Muntenescu" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" + }, + { + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185729, - "name": "Gradle" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 0 + } + ] + }, + { + "slotStart": "18:15:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "0e2cc89b-b559-4432-8df6-6e880873a92f", + "title": "Karaoke and Halloween Costume Party!", + "description": null, + "startsAt": "2024-10-31T18:15:00", + "endsAt": "2024-10-31T19:35:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + } + ] + } + ] + }, + { + "date": "2024-11-01T00:00:00", + "isDefault": false, + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "sessions": [ + { + "id": "8586de07-b3e8-4ebe-8212-f54e5f817f7d", + "title": "Registration & Check-In", + "description": null, + "startsAt": "2024-11-01T08:20:00", + "endsAt": "2024-11-01T09:20:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "759067", + "title": "The Future of Android, Kotlin, and Everything", + "description": "Modern mobile and Android are a touch over 15 years old and Droidcon London 2024 is also in its 15th year. \r\n\r\nSo, it’s time to look forward.\r\n\r\nTech trends come and go. Some burn hot, then disappear. Some become essential. The web is essential. Mobile is essential. Whatever happens with mobile technology, the idea that mobile won’t be as, or more essential, 15 years from now is nonsense.\r\n\r\nHow mobile is developed and delivered is a different question altogether. This will be a talk about near-term tech possibilities, but not what mobile might look like in 15 years. I could speculate, and I'd be wrong. It will be a talk about how this community can shape that future, starting now.\r\n\r\nWhile the tech hype machine has hopped from one shiny concept to another, neither the web nor mobile has remained static. Far from it. We’re in a moment of change and opportunity. A very special moment for this community specifically. But the community is necessary to take advantage of it.", + "startsAt": "2024-11-01T09:20:00", + "endsAt": "2024-11-01T10:00:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264357, + "name": "Keynote" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "6876a148-9098-4d59-809f-995afbb7b110", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T10:00:00", + "endsAt": "2024-11-01T10:20:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "733921", + "title": "Effective App Monitoring in Production", + "description": "Your feature is complete and ready to be shipped to production. Now you can forget about it completely and move onto the next thing, right? Actually the fun begins when your feature reaches your users. Is your feature healthy? Are your users seeing any issues? Which ones? How many?\r\nIn this session, we’ll discuss how to monitor Android apps in production to ensure they stand resilient and ready for the challenges of the real world. \r\n\r\nIn this session, you’ll learn how to design and report relevant events that matter to you and your team. If you are introducing monitoring in your application, you will learn how to design a tracking strategy to effectively monitor the application health and debug any issue users experience. If you already have monitoring in place, you will see techniques on how to get visibility into the data through the definition of KPIs (Key Performance Indicators) and dashboards. We will also provide examples of common problems teams face when implementing constant monitoring like knowledge sharing, alerting, and escalating issues to other teams.\r\n\r\nYou’ll see from real world examples how to track, detect and address production issues before they escalate.\r\n\r\nLevel up your app observability, delight your users and deliver with confidence with these tips.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "18c45d50-3e38-4933-a55b-8a833f850115", + "name": "Alejandra Stamato" + }, + { + "id": "084a1449-47c9-488c-b5b6-5b8534c45a23", + "name": "Mauro Frezza" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "cfcf5ef1-b7f7-46c2-82e1-ff3002c842e1", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T11:00:00", + "endsAt": "2024-11-01T11:15:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "736477", + "title": "Creating a Custom Compose Layout, Step-by-Step", + "description": "Building a custom layout in Jetpack Compose may seem intimidating, but it does not have to be. With an understanding of the tools available for custom layout, the phases of composition, and best practices for state management, anyone can create a powerful, beautiful custom layout.\r\n\r\nIn this talk, we will build a sophisticated schedule component step-by-step. This composable will include multiple display modes, theme-like encapsulation of styling and behaviors, scoped modifiers for easy customization, and even some animation.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "2d41d3fb-3321-4b47-a401-ae84a5de2423", + "name": "Huyen Tue Dao" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "2e322db2-0dcf-458b-9dc2-c6a62337a8e4", + "title": "Lunch", + "description": null, + "startsAt": "2024-11-01T11:55:00", + "endsAt": "2024-11-01T12:55:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "700387", + "title": "Mistakes you make using Kotlin Coroutines", + "description": "Kotlin Coroutines offer a powerful, yet deceptively simple solution for managing asynchronous tasks. However, their ease of use can sometimes lead us into unforeseen pitfalls. From unexpected cancellations to perpetually running operations, developers often encounter bewildering behaviors that can affect application performance and reliability. In this presentation, we'll dive deep into the common mistakes made while using Kotlin Coroutines. We'll explore why these issues arise, how to diagnose them, and, most importantly, how to resolve them effectively. Expect to walk away with advanced insights and practical strategies to harness the full potential of coroutines in your Kotlin applications, ensuring they're robust, efficient, and maintainable.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "ef7c1115-d852-44b9-9f72-530ba7223c55", + "name": "Marcin Moskala" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264386, + "name": "Coroutines" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": 39640, - "name": "Hopper", - "session": { - "id": "530160", - "title": "From Complex to Seamless - Succeeding in codebase migrations", - "description": "Complex changes require careful planning and execution - from gathering product requirements and system design to implementation. Introduce variables, like timezone differences, varying levels of technical and domain knowledge and a newly formed team and the complexity deepens. \r\n\r\nAt Skyscanner we were faced with all of these difficulties in our migration of our Hotels product from React Native to Native. This talk will take you through our journey and how we not only overcame these hurdles, but leveraged them to our advantage. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ - { - "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", - "name": "Maria Neumayer" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185700, - "name": "Intermediate" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185704, - "name": "Session" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - } - ], - "sort": 4 - } - ], - "roomId": 39640, - "room": "Hopper", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 3 + "id": "7370ab67-e2bb-4288-8de1-d5012f5278b2", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T13:35:00", + "endsAt": "2024-11-01T13:50:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": 39641, - "name": "Booth", - "session": { - "id": "504429", - "title": "Deep-Dive Into Mobile Accessibility & Interactive Quiz", - "description": "Nowadays mobile apps are at the heart of our interactions with the world around us. But are we sure that our apps are usable for everyone ? As developers, how can we make sure that the experience of our screen reader users is the best ?\r\n\r\nDuring this presentation, you will compete with the audience to measure your mobile accessibility knowledge, and learn at the same time how to optimize your application user experience for screen readers. \r\n\r\nWe will go deeper than the usual contentDescription to improve accessibility. For example by using Custom Action or Long Click to reduce the number of Talkback gestures needed to go through one screen. We will also open doors to new gestures to improve the UX of infinite horizontal lists like in Spotify or Netflix, that can be a trap for screen readers. \r\n\r\nPrepare yourself to learn new tricks that will make the usability of your app better for user with a screen reader than for sighted users.\r\n\r\nBonus 🎁 : The highest score to the quiz in the audience will win a small prize.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ - { - "id": "0e28eaf0-f8b6-4475-a3be-f3ce49f2d1ec", - "name": "Fanny Demey" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185699, - "name": "Introductory and overview" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185704, - "name": "Session" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185714, - "name": "Accessibility" - } - ], - "sort": 4 - } - ], - "roomId": 39641, - "room": "Booth", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 4 + "id": "733770", + "title": "User Initiated Data Transfer Jobs", + "description": "Google introduced a new api UIDT in Android 14 as an alternative to Work Manager and Foreground Services.\r\n\r\nA deep dive session into how your apps can run longer-duration, user-initiated transferring of data, such as downloading a file from a remote server using UIDT. With increasing restrictions on running Foreground Services it is very important to migrate your app's critical use cases to alternate API's. Android 14 applies strict rules on when you can run a Foreground Service & Android 15 is bringing 6 hours timeout to Foreground Services.\r\n\r\nWill share how specific use cases migrated to UIDT can lead to improved app performance.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "edd46ee9-3e55-478c-8f27-c8f86ca77138", + "name": "Tushar Varshney" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": 39642, - "name": "Lamarr", - "session": { - "id": "517710", - "title": "Introduction to Game Development with Flutter and Flame", - "description": "This session will be purely based on Game Development in Flutter with help of Flutter. \r\nI will start with a basic introduction to the Flame engine and some of the concepts of Game Development like Folder Structure, Game Loop, Collision Detection, adding objects, and giving a piece of awesome music to our game. \r\n\r\nIt will be a Shooting game or a Platformer game, but it will cover introductory to intermediate topics of Game Development in Flutter with Flame Engine. \r\n\r\nThis will really help users to explore the Game Development sector of Flutter and Flame.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ - { - "id": "d3469e78-7d62-4945-be1f-b9b3cc4a134b", - "name": "Shree Bhagwat" - } - ], - "categories": [ - { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185700, - "name": "Intermediate" - } - ], - "sort": 0 - }, - { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185704, - "name": "Session" - } - ], - "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], - "sort": 4 - } - ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - } - ] - }, - { - "date": "2023-10-27T00:00:00", - "isDefault": false, - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "sessions": [ + "id": "2cf43443-53aa-439d-b2e3-b3cb3f96c326", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T14:30:00", + "endsAt": "2024-11-01T14:50:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "693397", + "title": "Crime Scene InvestiGITor", + "description": "THERE HAS BEEN A MURDER!\r\n...\r\n(Or whatever the code version of a murder is... like breaking unit tests, a bug maybe?... I dunno!)\r\n\r\nTogether, we will learn how to become a professional and revered investiGITor, who will be able to sniff out and solve any version control offence with the ease and panache of a seasoned detective!\r\n\r\nVersion control software is often a mysterious black-box that we HAVE TO interact with in order to successfully collaborate with others. But what if it doesn't need to be a confusing & complicated enigma?\r\n\r\nYou will learn to probe into the dark recesses of Git and understand its inner workings by learning how to carry out many tasks that you will undoubtedly need to perform at some point in your career.\r\n\r\nFor example:\r\n* Ever needed to safely remove a secret that you accidentally stored in the repository?\r\n* Ever had your app break, not know why, and then needed to quickly hunt down the exact commit where a bug was introduced?\r\n* Ever needed to travel back in time, through history, to stop a crime before it even happens...(sort of)?\r\n\r\n...All these things and a bunch more useful & interesting, lesser-known ways to become a masterful git detective and truly understand the most important tool in a developer's arsenal.\r\n\r\nSo come have a little fun with me in this talk, bring the bugs to justice, solve the case of the naughty commit & become a hero by defending the integrity of your codebase!", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", + "name": "Benjamin Kadel" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264369, + "name": "Other" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": "e167a3bd-c503-4ed8-b56f-ec30897857ee", - "title": "Registration & Check-In", + "id": "c1cc8017-64f6-44c1-9c63-ac4116e75ea0", + "title": "Break", "description": null, - "startsAt": "2023-10-27T08:00:00", - "endsAt": "2023-10-27T09:00:00", + "startsAt": "2024-11-01T15:30:00", + "endsAt": "2024-11-01T15:45:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "538663", - "title": "Fireside Chat with Googlers", - "description": null, - "startsAt": "2023-10-27T09:00:00", - "endsAt": "2023-10-27T09:50:00", + "id": "717692", + "title": "Level up your SDKs with KMP - no rewrite required!", + "description": "There are several compelling reasons to explore adding Kotlin Multiplatform (KMP) to your existing SDKs or libraries. The main one - to reduce development time and cut maintenance costs by having a single codebase for shared logic.\r\n\r\nBut what if you already have a suite of tried and tested native SDKs, and want to simply add KMP capabilities to broaden your audience, without impacting existing customers? Or perhaps you’re adding functionality on top of your SDKs and want to build it once instead of duplicating it in 2, 3, or 4 programming languages?\r\n\r\nI’ll share our experiences at PubNub with bringing three of our realtime messaging SDKs - TypeScript, Swift and JVM, under a unified KMP API. I’ll also show how this foundation then enabled us to build and ship a new Chat SDK on top, built with KMP from the ground up and targeting all 3 platforms.\r\n\r\nThe talk will cover the initial requirements phase, the architectural decisions, and the tradeoffs that we had to make to finally land on KMP as the chosen solution.\r\n\r\nWe'll then delve into practical code examples and solutions (as well as quite a few WTF moments!) that allowed us to build the Chat SDK without affecting our existing users, and even improved the quality and consistency of our native SDKs.\r\n\r\n", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, - "isPlenumSession": true, + "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "ffd6d0b9-9615-4f5a-9f58-6dcf3d4acb3b", + "name": "Wojtek Kaliciński" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185706, - "name": "panel" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264374, + "name": "API" + }, + { + "id": 264381, + "name": "Cross-Platform" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "84ad59e0-d9b4-46a9-a22a-0dfdd70342e2", + "id": "059a58e3-50b1-4a62-8377-7263b7ca960f", "title": "Break", "description": null, - "startsAt": "2023-10-27T09:50:00", - "endsAt": "2023-10-27T10:15:00", + "startsAt": "2024-11-01T16:25:00", + "endsAt": "2024-11-01T16:40:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "524652", - "title": "Peeling Back the Layers: Unmasking the UI-nknown!", - "description": "How much do you know about the UI layer and its best practices? What's the preferred way to produce UiState? How to consume it? Should you use MVVM or MVI? What about handling UI events? Where to hoist a particular state? What types of state holders can you have in your app? Should the ViewModel contain all the logic that comes from the UI? What are its limits?\r\n\r\nAll these questions and more about the UI layer will be answered in the talk. Level up your UI layer knowledge and make it a well-known area. We'll use real code snippets to show how to apply the recommended practices in an Android app.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "732470", + "title": "Exploring Kotlin Symbol Processing: A Practical Guide", + "description": "In this session we’ll dive into the world of Kotlin Symbol Processing (KSP). This session aims to provide an introduction to KSP and its benefits compared to the Kotlin Annotation Processing Tool (KAPT).\r\n\r\nThe practical portion of this talk will guide you through the process of creating annotation definitions and implementing a symbol processor. We will demonstrate the usage of KSP API and KotlinPoet for generating Kotlin files, providing you with a hands-on experience of working with KSP. Furthermore, we will demonstrate how to use KSP in multiplatform projects.\r\n\r\nBy the end of this talk, you will walk away with a solid understanding of Kotlin Symbol Processing, and practical knowledge on how to leverage KSP in your development workflow.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b4cf78c9-623b-4b8f-b676-097fb7802668", - "name": "Manuel Vivo" + "id": "f589d95b-8262-4337-b6c8-78b4701afa3a", + "name": "Dean Djermanović" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, + "id": 264359, "name": "Kotlin" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "2b8aba4e-4ee3-47e4-9972-29c3166c1c3a", - "title": "Break", + "id": "c9009c93-af8c-4487-ae2f-253f17acea6a", + "title": "See you next year!", "description": null, - "startsAt": "2023-10-27T10:55:00", - "endsAt": "2023-10-27T11:10:00", + "startsAt": "2024-11-01T17:20:00", + "endsAt": "2024-11-01T17:30:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null - }, + "status": null, + "isInformed": false, + "isConfirmed": false + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 49271, + "name": "Glass", + "sessions": [ { - "id": "538655", - "title": "Practical Magic with Animations in Compose", - "description": "Are you constantly in awe of the animations your designer creates but have no idea how to implement them? Or maybe you want to keep your users engaged by adding delightful little treats in your app... There are a few key principles that unlock a wide range of different animations, from basic to advanced.\r\n\r\nWe will go through some practical examples of how to implement animations in Jetpack Compose, such as working with gestures to control animations and how to do state based animations. Join us to learn how to think about implementing any animation in a step-by-step way. ", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "696374", + "title": "AndroidX Lifecycle's Path to Multiplatform", + "description": "We’ve recently converted the AndroidX Lifecycle libraries (ViewModel, Lifecycle Runtime, and Compose support) to Kotlin multi-platform (KMP). Join this session to learn more about how this process went, what the real-world challenges of maintaining API backward compatibility are, what lessons we learned from working around KMP limitations, and insights to guide you in migrating your own libraries to KMP.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - { - "id": "98fa317f-118e-4210-86a0-6d6516600458", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T11:50:00", - "endsAt": "2023-10-27T12:05:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "513549", - "title": "Blast Off: Managing Hundreds of UI Updates for Emoji Cannons", - "description": "Managing a state might be a challenge. Managing the state with hundreds of updates and constant recomposition of floating emojis is a challenge indeed. In this talk, I will share how to build emoji cannon that floods your screen with UI elements, update the state, and does not freeze your UI. All of that with Jetpack Compose :) \r\nIn addition to covering the technical aspects of building and managing a state, I will also share how to build a responsive UI that takes the user input and animates.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "747072", + "title": "Scalable Testing Strategies", + "description": "It's very easy to have too many tests and lose track of what kind of feedback each one brings. Moreover, testing on multiple devices can get expensive, both in terms of CI cost and lost productivity waiting for tests to pass. In this talk we'll talk about how the testing pyramid has evolved because of new requirements and features that need to be tested, such as multiple form factors, new configuration changes, Jetpack Compose, screenshot testing, etc.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b6dc4b56-ca5d-4e55-8a57-e37f6e6d6af0", - "name": "Piotr Prus" + "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", + "name": "Jose Alcérreca" + }, + { + "id": "532dc16d-b69d-4f68-aa49-fe9878b0b52f", + "name": "Adarsh Fernando" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264363, + "name": "Testing" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "a0f5131a-50a1-4336-a76f-9db2d9e89524", - "title": "Lunch", - "description": null, - "startsAt": "2023-10-27T12:25:00", - "endsAt": "2023-10-27T13:25:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "id": "736513", + "title": "A Snapshot Preview of Paparazzi 2.0", + "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this session, we'll discuss:\r\n* why pixel perfection is challenging af\r\n* image encoding and why we chose APNG for animations\r\n* why Google's publishing of layoutlib is great for the community\r\n* ...and more!\r\n\r\nWe'll also give a sneak peek to what's coming in 2.0, as well as why we're excited about it and you should be too!", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", + "name": "John Rodriguez" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264380, + "name": "Tooling" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "538652", - "title": "Why is my app letterboxed?!", - "description": "Have you ever wondered why your app is running in a small window in the middle of the screen, with bars on the sides? This usually happens on larger screens, but why?\r\n\r\nThis is called letterboxing, which is an Android app compatibility mode the platform places an app in if it cannot support a certain window size. In this talk, we will explore why letterboxing happens and how to optimize your app to avoid it. We will also discuss how to ensure that your app looks good on all screens, regardless of the screen orientation or aspect ratio.\r\n\r\nBy the end of this talk, you will be able to:\r\n- Identify the causes of apps running in letterbox mode.\r\n- Understand how to fix this issue.\r\n- Ensure that your app looks good on all devices.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734509", + "title": "Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components", + "description": "Are your ViewModels exponentially growing out of control as they manage the state for each of your Composables? This talk introduces Molecule, a new library for creating state holders in Jetpack Compose. We’ll explore how Molecule can simplify presentation state management in Compose, allowing for more flexible and scalable UI development. Attendees will learn how to implement Molecule in their projects and understand its advantages over traditional state management approaches as well as some of the drawbacks to this approach.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7d912e80-f02c-42cf-92db-98e670bef1a6", - "name": "Roberto Orgiu" + "id": "6624cb6c-7fc4-4495-a87f-8060e88d7d18", + "name": "Jack Adams" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185735, - "name": "Android" + "id": 264362, + "name": "Flow" }, { - "id": 185737, - "name": "Coroutines" + "id": 264364, + "name": "Compose" }, { - "id": 185739, + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264388, "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - { - "id": "ced93252-06e3-4f68-8af3-afd0bbc137b7", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T14:05:00", - "endsAt": "2023-10-27T14:20:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "520809", - "title": "TextField in Jetpack Compose: past, present and future", - "description": "Text input is a fundamental text component used in practically all apps at some point, from simple usage in a login screen to complex dynamic server-driven forms.\r\nIn this talk we’ll recap current text input APIs and their shortcomings, in particular for state management. As well we’ll explore the brand new BasicTextField2 API surface for common scenarios along with filtering, selection, gestures and more. The API you've been waiting for is coming to you soon :)", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "720685", + "title": "Why is adaptive layout a nightmare?", + "description": "Dive into the captivating realm of Android UI development in this session where we will share in-depth expertise on designing versatile user interfaces. Together, we will explore the complex challenges of organizing and building UI components, providing unique insights into crafting seamless user experiences across a variety of devices such as phones, foldables, and tablets.\r\n\r\nWhile Google advocates for the use of adaptive layout, we will unveil the often underestimated nuances, shed practical light on overcoming obstacles you’ll meet down the road and help you create truly adaptive Android applications.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "18c45d50-3e38-4933-a55b-8a833f850115", - "name": "Alejandra Stamato" + "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", + "name": "Gerard Paligot" }, { - "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", - "name": "Anastasia Soboleva" + "id": "81f9ecbf-c08c-48c7-850f-1e79e1439867", + "name": "David Ta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185728, + "id": 264364, + "name": "Compose" + }, + { + "id": 264367, + "name": "Foldables" + }, + { + "id": 264377, "name": "Modern Android Development" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - { - "id": "413af535-507d-4b62-954f-ebfd58548f09", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T15:00:00", - "endsAt": "2023-10-27T15:15:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "496334", - "title": "90s Website … in 2023 on mobile in Compose … for science", - "description": "Why would anyone build something with a 90s website aesthetic - in Compose? Nostalgia? For one the 90s website aesthetic made heavy use of animations and visual effects. So emulating this style will take any animation framework through its paces. This talk will take some of the most iconic 90s website elements and demonstrate how to reproduce them to build a retro mobile experience using Jetpack Compose. \r\n\r\nI can’t promise the end result will have a good user experience but I can promise a good look at the animation system in Compose and a collection of cheesy code snippets.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "730918", + "title": "Upgrading Meta's Kotlin Infrastructure to K2: A Migration Journey", + "description": "In this talk, we will share our experience migrating Meta's Kotlin infrastructure from K1 to K2. We will discuss the challenges we faced during the migration process, including differences in error handling between K1 and K2, and how we overcame them. We will also cover the process of migrating compiler plugins, including overview of plugins we migrated and the solutions we implemented to overcome challenges. Finally, we will discuss our experience with the KSP2.0 migration.\r\nThis talk will provide valuable insights for developers and teams considering a migration to K2, as well as those interested in learning more about the challenges and solutions involved in large-scale migrations.\r\n\r\nKeywords: K2, Kotlin 2.0, Kotlin compiler plugins", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "2313d8eb-7d6e-444d-8e8d-4d223f896ce8", - "name": "Maia Grotepass" + "id": "33801296-8d41-4df0-b729-c0461afa1be2", + "name": "Ruslan Latypov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264374, + "name": "API" + }, + { + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - { - "id": "b90c49a2-817f-4b03-9339-f93257d0e7e5", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T15:55:00", - "endsAt": "2023-10-27T16:10:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "538661", - "title": "A guide to using foreground services and background work in Android 14", - "description": "Coroutines, WorkManager, Foreground Services, Oh my! \r\nHave you ever felt confused on how to ensure your application is kept alive to continue work after it's left the foreground?\r\n\r\nThere have been many changes over the past few Android releases regarding the background work and Foreground Services. In this talk we will break down: \r\n- What is background work vs foreground service vs asynchronous work?\r\n- What’s new with Foreground Services in Android 14?\r\n- We'll provide a framework to help you decide what API to use in Android 14\r\n- We'll go over some common misconceptions around WorkManager or Foreground Services\r\n\r\nAs a bonus we’ll also discuss what the future of managing background work and Foreground Services will look like beyond Android 14.", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "735915", + "title": "Android driver - application of Android device in RC vehicle development", + "description": "TL;DR: This talk will showcase development of remote controlled vehicle with Android powered device as its central unit. \r\n\r\n... and now long version:\r\nStudents and researches around the world often struggle in early stages of development of robotics and hardware projects. The reason? High cost and connection difficulties of all the sensors and effectors for Arduinos, Raspberry Pi's or whatever platform they do choose. All of them have a solution right in their hands - or their pockets - and that is with almost no additional costs. \r\n\r\nBy enabling Android device to act as a part, or center, of embedded system you can unlock vast variety or its capabilities - ones that would cost hundreds of dollars to purchase and dozens of hours to connect & set up. Some of them are:\r\n- LTE access to Internet\r\n- WiFi & bluetooth connectivity\r\n- screen, speakers, microphone, LED light\r\n- accelerometer, gyroscope\r\n- multiple cameras\r\n- biometric sensors\r\nNot to mention higher level of services they offer through countless libraries ready to be used by developers.\r\n\r\n-> By the end of this talk, attendees will discover an innovative & easy way to use Android powered devices to cut down costs of prototyping hardware projects, especially as proposed, by building RC vehicle. ", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "05c58f24-23c5-4bce-a4f5-9b610f7084d3", - "name": "Alice Yuan" + "id": "58038e33-703e-497c-9f47-3624e36418e8", + "name": "Tomasz Słuszniak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185728, - "name": "Modern Android Development" + "id": 264366, + "name": "Modularization" }, { - "id": 185735, - "name": "Android" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185739, - "name": "Libraries" + "id": 264369, + "name": "Other" + }, + { + "id": 264370, + "name": "IoT" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false }, { - "id": 36717, - "name": "Hamilton", + "id": 49272, + "name": "Things", "sessions": [ { - "id": "529751", - "title": "Performance in Compose: Lessons learned from Lazy layouts", - "description": "The Compose team focused on performance extensively over the last few years. Many of the findings are documented and shared in different forms, but optimization continues to be one of the arcane arts of Compose. \r\n\r\nIn this talk, Andrei shares his own experience on optimizing Lazy layouts, focusing on the most notable tips that might help your application performance. He will provide a detailed description of Compose performance characteristics, summarizing a year long team effort to improve (re-)composition performance. Aside from the theory, you’ll also get practical tips about improving your composables derived from the experience of the Compose team.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "696208", + "title": "Deep Dive into the Compose Compiler", + "description": "Have you ever wondered how the slot table is implemented in Compose? How are groups generated? What's the magic behind skipping, and how can you peek inside a slot table? The Slot table implementation was updated recently for performance. This talk takes a deep dive into the Jetpack Compose compiler internals, with a special focus on answering these questions and exploring the implementation details of the slot table. We'll go beyond the basics, uncovering how the compiler is structured and tested. This knowledge will empower you to understand, contribute to, or even build your own tools on top of it. Whether you're a curious developer or aspiring plugin contributor, get ready to fully understand the Jetpack Compose Compiler.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1e19e15d-18c9-4cd9-82b2-280120a8c8a6", - "name": "Andrei Shikov" + "id": "a79f3682-df5c-40e4-8aa8-1b6eaa7bdeec", + "name": "Mohit Sarveiya" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, + "id": 264359, "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "520549", - "title": "Composing an API the *right* way", - "description": "Everyone who writes code using Jetpack Compose designs Composable functions and components all the time. In this talk, we'll take a look at some highlights from the official guidelines around designing Compose APIs, to see how we can do a better job of building with Compose.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "735816", + "title": "Developing Trusted Applications for Trusted Execution Environments and vTEEs", + "description": "In today's mobile-first world, security is paramount. Join us in this engaging session where we introduce vTEE, a groundbreaking concept designed to embed advanced security into mobile applications. This session will demonstrate how vTEE allows developers to create their own Trusted Execution Environments (TEEs) directly within their apps, ensuring that sensitive data remains protected. Additionally, learn how our global award-winning open-source project, jCardSim, simplifies the prototyping and development of trusted applications, accelerating your journey towards secure, scalable, and innovative app experiences. Get ready to supercharge your app security with ease and flexibility!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", - "name": "Márton Braun" + "id": "d7e7d278-a645-42c5-b337-c793a5b5bd79", + "name": "Mikhail Dudarev" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185708, - "name": "Kotlin" + "id": 264372, + "name": "Security" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264381, + "name": "Cross-Platform" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "502207", - "title": "Take your shot of Vitamin!", - "description": "Decathlon has more than 160 frontend products, including 50 dedicated to mobile applications. Due to this context, it is hard to align the user interface across all these projects while respecting the platform.\r\n\r\nVitamin is a Design System developed by Decathlon as a product which can be adapted to any context and with multiple technical implementations for Android, iOS and Web. In theory, you can use this Design System in your application and customize it to fit your theme and your needs.\r\n\r\nIn this presentation, I'll focus on Vitamin Compose, the design and technical architecture, biases and what are the next steps.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "731650", + "title": "Elevated Dependency Injection: Going Beyond the Basics with Custom Hilt Components", + "description": "Hilt/Dagger is a popular and powerful building block in Modern Android Development. Its benefits include reduced boilerplate and compile-time magic to ensure runtime safety and performance. \r\n\r\nBy default, Hilt provides basic components, such as Singleton, Activity, or ViewModel. \r\nIt is also extensible, allowing you to create additional components for more complex use cases. \r\n\r\nHere at Patreon, building a custom UserComponent tied to a user session has improved our codebase immensely by reducing boilerplate, increasing code safety, and making it easier to cancel work when a user logs out.\r\n\r\nThis talk will focus on the ins and outs of custom components and some simple but non-obvious techniques that we’ve used to make them nicely integrate into our app!\r\n\r\nHighlights of the talk will include:\r\n\r\n- Custom components and when you should use them\r\n- The beautiful synergy of dagger components and coroutine scopes\r\n- Overcoming the limitations of Dagger’s single inheritance and Hilt’s fixed component hierarchy\r\n- Providing easy access to bindings in `@HiltViewModel` and `@HiltAndroidTest` classes\r\n- A case study of Patreon’s `UserComponent`", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", - "name": "Gerard Paligot" + "id": "1caff1f9-9594-4ce8-a031-ed1ba2201d13", + "name": "Steven Kideckel" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185736, - "name": "Design" + "id": 264384, + "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264387, + "name": "Dagger" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495137", - "title": "Practical ADB usage to enhance your life!", - "description": "ADB is an incredibly underutilized tool that can dramatically improve your Android development experience! Maybe you know some basic ADB commands, perhaps even a couple of nifty advanced ones or possibly, you don’t know what ADB even is!\r\n\r\nImagine automatically & swiftly logging into your app’s test account, instantly inspecting your app’s current Activity/Fragment stack or editing your app’s shared preferences & phone settings all without touching your device! All this and SO MUCH MORE is possible thanks to ADB.\r\n\r\nThis session is not intended to blast you with a list of ADB commands but instead to share a collection of practical scenarios & examples how you can use this supreme tool everyday in a variety of ACTUALLY useful ways. \r\n\r\nJoin me in this session, regardless of your experience, as we explore what this tool can really do to change the way you work forever!", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "736447", + "title": "You don't have to run it locally! How to run your emulators in the cloud.", + "description": "Probably every Android engineer has tried running Android Emulator on their laptops. Many companies have experience running Android Emulators in CI pipelines for testing. But what does it take to run a highly interactive emulator in the cloud? Why would one need to run an emulator in the cloud?\r\n\r\nAt Uber we know how to build Cloud Development Environments. And now we expanded into Android Emulator space. Providing our engineers with zero setup emulators. \r\n\r\nWe'll walk you through all the moving parts of the emulator setup in a dev environment that sparks joy. Share the challenges we had and opportunities we discovered.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", - "name": "Benjamin Kadel" + "id": "695e56a6-62a8-496b-9082-8cc4dadec337", + "name": "Petras Vičiūnas" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185731, + "id": 264380, "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "518266", - "title": "Making Data Visualizations More Accessible - Lessons Learned", - "description": "Graphs are a great way to visualize different types of data. But not everyone can consume them the same way - and if you add, e.g., touch interactions, not everyone can use the pointer the same way. Also, not everyone understands graphs the same way - due to prior familiarity, disability, or other reasons. And what if you can't see at all? That is another factor that adds complexity to data visualizations.\r\n\r\nIn this talk, I will share some tips and demonstrate how you can improve the accessibility of your graphs so that they work for different types of users - whether they are using assistive technologies or not. You'll get actionable advice to take to your apps and improve their accessibility immediately. You will learn about adding text alternatives for data visualization, adding alternatives for touch, and assuring that color is not the only way to communicate information.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "734218", + "title": "Why WhatsApp uses ListView", + "description": "Using outdated ListView on one of the most used screens screens sounds outrageous. Will talk about technical and organisational reasons why and how WhatsApp still uses ListView and what are the challenges preventing migration. ", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", - "name": "Eeva-Jonna Panula" + "id": "e11a53fd-1cfb-4930-9dfa-42187e94d869", + "name": "Hadi Tok" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264360, + "name": "Soft Skills" }, { - "id": 185714, - "name": "Accessibility" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "527245", - "title": "Multiplatform USB: How to Communicate Seamlessly", - "description": "At the droidcon Berlin we as the GDG Berlin Android introduced 'ZeBadge' a digital name badge, programmed by a companion Android app.\r\n\r\nSadly it was limited to only Android companions. But not anymore! Let me introduce ZeKompanion: A multiplatform app that communicates with ZeBadge, no matter the platform (except for iOS).\r\n\r\nThis talk will guide through the architecture of the app, describing the abstractions taken and explain how you can leverage Kotlin Multiplatform for Mobile to abstract the platform specific USB communication from the Compose UI displayed on all devices.\r\n\r\nFollow here for an interesting journey about USB Serial communication, Kotlin and KMM, Jetpack Compose and how to boil it all together into one app: ZeKompanion.\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "731808", + "title": "Streamlining Permission Request in Jetpack Compose", + "description": "Managing Android permission requests often involve considerable boilerplate code, and while Jetpack Compose offers many advantages, it has not simplified this aspect. It can add complexity if not properly designed. This talk will demonstrate an efficient method for creating a reusable Android permission request flow with minimal boilerplate using Jetpack Compose. By the end of this session, you will learn how to implement a reusable permission request handler for single and multiple permissions. Additionally, you'll discover how to provide custom permission rationale messages and design a custom permission request UI with minimal boilerplates, enhancing developer experience and application usability.\r\n\r\nKey Takeaways\r\n1. Understanding Permission Management in Android: A quick overview of the challenges in handling permission requests within the context of Android development.\r\n\r\n2. Identifying Boilerplate in Permission Requests: Examination of the typical boilerplate code associated with permission requests in Jetpack Compose.\r\n\r\n3. Efficient Permission Handling: Step-by-step guide on creating a reusable permission request handler for single and multiple permissions.\r\n\r\n4. Customization Techniques: How to implement custom permission rationale messages and design custom UI components for permission requests.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "fc80cf34-563e-4cd4-a04d-23d1191a7812", - "name": "Mario Bodemann" + "id": "3b95e79a-0e6b-4fcd-b44e-023fa3dfd7cd", + "name": "Mayowa Egbewunmi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185710, - "name": "KMP" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185720, - "name": "IoT" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185730, - "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530286", - "title": "From Laptop Builds to Advanced CI", - "description": "How do you transition from the solo-coder mindset to building a robust, automated CI pipeline that supercharges your team?\r\n\r\nOnce you get a basic pipeline running there are numerous aspects to evaluate:\r\n\r\n* Are the results useful?\r\n* You start to add unit test results and collect build artifacts. Is it fast and reliable enough that the team benefits from it?\r\n* Do you need a merge queue or do you need better checks?\r\n* What is consistently missed that you can automate?\r\n* What tedious repetitive tasks can you transform into a welcome resource?\r\n* Are you hitting resource limits that require an engineering investment to mitigate?\r\n* Do you invest in Gradle or Bazel? What JVM options are right for your codebase?\r\n* Is Docker useful? How far should you go in your optimizations?\r\n* How do you secure the pipeline?\r\n \r\nWe'll delve into each topic and share how to apply our learnings to empower you. Along the way we will discuss how to approach stakeholders outside engineering to demonstrate the value it brings to a business. Join us for a saga of struggles and victories and how we transformed our CI pipeline at a modern scale-up business.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "734441", + "title": "A journey in Android's BLE world", + "description": "Working with BLE on Android could be overwhelming, there are a lot of OSS libraries to pick from, different permissions to be declared, and sometimes different behaviors depending on the Android version and device.\r\n\r\nThis talk aims to make order between what's available out there: we'll start from the basics of how BLE works, climbing the ladder and going more high-level examining tools and libraries using Kotlin features to simplify observing data and freeing up unused resources, including an exploration of latest JetPack library AndroidX Bluetooth.\r\n\r\nYou'll find real-life scenarios, examples, and strange issues you may encounter (along with even stranger fixes).\r\n\r\nWhether you plan to integrate a BLE device in your app, improve the existing code to be more expressive and use modern libraries, or even only scan for Bluetooth beacons, follow me on this journey in the BLE world.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "96e519b2-b0e1-4232-a71c-21bf3145cb13", - "name": "Jason Pearson" + "id": "8253d551-1437-4a3e-b4f4-56bf73373109", + "name": "Paolo Rotolo" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185716, - "name": "Modularization" + "id": 264374, + "name": "API" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" }, { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185730, - "name": "Android Studio" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false }, { - "id": 36718, - "name": "Liskov", + "id": 53981, + "name": "Stadia", "sessions": [ { - "id": "527210", - "title": "Bluetooth Magic, first level", - "description": "We use it all the time, but what actually is Bluetooth and how do we add it to our apps? What is the magic behind it? \r\nJoin me in this talk to go over all the components required for assembling your own Bluetooth feature, how to discover devices, how to transfer data among them and what different options do we have. All of this grounded with examples, tips and tricks coming from my own experience with it.\r\nAfter this talk, you should have the base knowledge to add Bluetooth to your apps. Because, sometimes, we all, need a little bit of... magic!", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "720892", + "title": "Seamless mobile real-time communication with WebRTC", + "description": "The WebRTC project allows developers to build smooth voice and video communication solutions by facilitating the transmission of data between peers.\r\nDuring this talk, we will explore the details of the WebRTC project, including its design principles, functionalities, and how it transforms communications for many web and mobile apps. \r\nFinally, we will create a simple mobile application in Kotlin Multiplatform using WebRTC so you will want to start a business and become a billionaire. \r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", - "name": "Nicole Terc" + "id": "4674e36f-50f8-4ffb-a486-679d730c759e", + "name": "Renaud Mathieu" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264364, + "name": "Compose" }, { - "id": 185719, - "name": "Other" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529068", - "title": "Demystifying Dagger", - "description": "Dagger gets a bad rap for being \"difficult to understand\", or \"overly complicated\", or \"black magic\". In reality, Dagger generates pretty straight-forward, elegant wiring code similar to how we would likely write it manually and, I believe, it's the complexity of large scale apps that makes it complex.\r\n\r\nIn this talk we'll look at different types of injection that Dagger supports, and the code it generates to do so, to get an understanding of what it's doing and how it works. We'll then dive into Components and Subcomponents to see how Dagger wires together our dependencies into a graph. Finally, we'll look at how Dagger can be extended using Anvil and other tools.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "722874", + "title": "From realtime CameraX filters to General Purpose GPU computing in simple steps", + "description": "Did you ever think that that the computational complexity of image processing is comparable to that of AI training, and both tasks use GPUs?\r\n\r\nModern phones have powerful GPUs that are typically used for photo, graphics and image processing which naturally brings us to OpenGL and Vulkan C++ APIs.\r\n\r\nLuckily, on Android we have OpenGL Java bindings, so we don't need to dive into C++. And combined with the CameraX effects API, we can build advanced filters for the camera. What is more, mobile GPUs are also not limited to these types of tasks and can be used for general purpose computing, even the ones completely unrelated to graphics.\r\n\r\nIn this session I will show you how to set up and write your own shaders for real-time image processing with CameraX. I will also show you how to make a step forward and use shaders for general purpose GPU computing, so that you can take advantage of the GPU power.\r\n\r\nCome to this talk and learn how to harness the computing power of your phone's GPU!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f1275dc9-492b-4ab1-b31b-3e19bccde628", - "name": "Ryan Harter" + "id": "892ec2dc-5b2e-45fb-9393-ff1407ebebe7", + "name": "Konstantin Zolotov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" + "id": 264369, + "name": "Other" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264384, + "name": "Android" }, { - "id": 185738, - "name": "Dagger" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495165", - "title": "How to handle incidents at scale", - "description": "In this session, I want to guide the audience into applying healthy processes to handle incidents. We have all probably been in the situation of having our apps unexpectedly crashing - either because of a bad release, because of unexpected Backend changes, etc.\r\n\r\nIn these scenarios, we usually get overwhelmed by messages from different departments (support folks, managers, and so on), this is especially true when operating at a large scale, and things can quickly get out of control.\r\n\r\nThis talk aims to propose rules that will not only mitigate the above scenarios but also improve the communication flow, give tips about how to set up alerting systems and what to measure, and talk about a \"post-mortem\" process.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "700492", + "title": "Ten things you heard about testing that might be wrong", + "description": "Testing became an essential part of Android development. Many conference talks have been given and even more best practices have been written. \r\n\r\nBut what if, as time evolved, some of the things we thought were true, changed?\r\n\r\nLet’s start questioning some of these in this talk:\r\n- Are flaky tests fixable?\r\n- Are mocks even harmful?\r\n- Is DI about testing?\r\n- Did we understand testing in isolation properly? \r\n- Is the test pyramid still valid?\r\n- Is Robolectric good or bad on Android?\r\n- And in times of AI, should we generate tests?\r\n\r\nCome and join my session to learn more!\r\n", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ac9cef8a-3072-4e52-8064-a5155be542b0", - "name": "Alessandro Mautone" + "id": "4d3a31b1-61e5-4a26-9ca3-c9f30e3eaa08", + "name": "Danny Preussler" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495999", - "title": "A Picture Is Worth A 1,000 ... Lines? Devs?: Get the full picture (on image loading) with Coil", - "description": "To download an image on Android, all you need are these lines of code:\r\n val input = URL(“https://cutecatsimage”).openStream()\r\n val bitmap = BitmapFactory.decodeStream(input)\r\n imageView.setImageBitmap(bitmap)\r\nWhy then, are there whole projects and teams dedicated to doing this on Android like Coil, Picasso, Glide, etc. In this talk we’ll focus on Coil and look under the hood to break down the inner workings of Coil’s image downloading pipeline. We’ll go way beyond using `imageViewload(“https://cutecatsimage”)`, and dive deep into Coil’s source code to understand how it handles loading your image into memory, caching, and transforming your image. You’ll walk away from this talk with a rich understanding of the journey the images you’re working with take to show up on your screen, and a being in a much better spot to work with problems that could arise.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734664", + "title": "Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.", + "description": "With the advent of Android 15, edge-to-edge design has become the default configuration. Consequently, applications must be capable of accommodating window insets, including the system status bar and navigation bar, as well as supporting drawing under display cutouts and other system UI elements. This presentation will address the most prevalent challenges encountered when supporting such a diverse range of UI configurations. Additionally, it will illustrate how to utilize Android previews and testing with Jetpack Compose to effectively navigate this complexity and guarantee an optimal user experience across all scenarios.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "a13934a7-6137-4a94-9a03-84919fce12f4", - "name": "Rafa Moreno" + "id": "257fcb0c-cc84-4892-a1f1-54985071e0a6", + "name": "Timo Drick" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264363, + "name": "Testing" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "495565", - "title": "Our ongoing journey from REST to GraphQL on Android", - "description": "Here is the story of an ongoing migration... and what a migration! This journey moving from our REST API to the GraphQL one is a long run with much coordination across all tech teams.\r\n\r\nThis return of experience will focus on how we are dealing with this transition on Android where Retrofit coexists with Apollo, how we synchronized with backend teams to keep our GraphQL schemas up-to-date or how we are dealing with our authentication stack with Apollo Kotlin, or the issues we faced and much more.\r\n\r\nBuckle up! Relax! The journey is just starting 🚜", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "741888", + "title": "Monetizing Your Side Project to $1k in Monthly Revenue and Beyond", + "description": "Only ~17% of apps reach $1k in monthly revenue. Learn best practices on crossing $1k MRR and beyond from personal experience building two profitable apps and advice from other successful app founders.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "462e6b97-01f9-44b1-864c-9d85bedf911b", - "name": "Julien Salvi" + "id": "0e4f3abe-cc6c-409a-8f34-e346f23c0cc4", + "name": "Jeffrey Bunn" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, + "id": 264360, + "name": "Soft Skills" + }, + { + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185724, - "name": "API" + "id": 264369, + "name": "Other" }, { - "id": 185735, - "name": "Android" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528020", - "title": "Trust no one - Introducing the Play Integrity API", - "description": "You put a lot of time and effort into developing your application, but you're then releasing it into the wild without any security measures in place. Your app could be modified and requests to your backend server could be coming from unknown and unsafe environments, which makes your services vulnerable to attack and abuse. The Play Integrity API helps your app's backend server to take appropriate actions to prevent attacks and reduce abuse by detecting potentially risky and fraudulent interactions.\r\nIn this talk we will cover the API with a focus on:\r\n- How to setup and use the API\r\n- Low-latency use cases with the new standard request\r\n- Migrating from the SafetyNet Attestation API\r\n- How to test and manage errors\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "735413", + "title": "Let's build a performance measuring tool from scratch", + "description": "Did you know you can measure the performance of production apps? Android is based on Linux, so that gives us a lot of power!\r\nThrough live-coding, let's have fun exploring system files, low-level events and the Android source code to see how we can gather metrics (FPS, CPU, RAM) on your favourite apps!\r\nYou'll gain more insights on how Android work under the hood and why measuring performance can be crucial but complex.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e5941ea5-5a10-45a0-903b-f642db802a16", - "name": "Pietro Maggi" + "id": "ae4dc258-34a6-462c-b778-807da7d25de4", + "name": "Alexandre Moureaux" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "510034", - "title": "Hot Take: Engineering Managers are actually useful!", - "description": "Is your manager pulling their weight, or just a waste of space? Should they exist just to protect the people doing the *actual work* from ignorant execs? \r\n\r\nTruthfully, managing any human is a science and an art, and managing engineers certainly isn't easier! A good manager is a leader, architect, consiglieri, and career coach… and yes, sometimes the person who moves JIRA tickets around a kanban board!\r\n\r\nIn this talk, we'll address some common hot takes about engineering management and talk about the transition from engineer to manager. Whether you want to become a manager or continue as an individual contributor, you'll leave with a better sense of what your manager actually does!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "706060", + "title": "Overcoming Unsecurities in WebViews", + "description": "Is your relationship with WebViews healthy? Sometimes you can't avoid the need to display web content in your app. It can be a functionality that you need to release quickly and it's already implemented by web devs in your team. It can be just a Terms and Conditions page you need to show. Often the reason for putting these into WebViews is that the latest version must be displayed without requiring an app update.\r\n\r\nSo web content tends to make its way into many apps. It's not obvious that by adding a single WebView, you can open up your app for abuse by malicious actors. Google made steady progress in making WebViews more secure by default but often you can't stop supporting those old, vulnerable OS versions. Ultimately it's your responsibility to secure your WebViews and the default settings are not always right. This talk aims to help with that while also highlighting security issues that lurk in those seemingly simple yet quite complex APIs.\r\n\r\nYou would learn the importance of always sanitizing inputs and restricting capabilities to what is actually needed. If you want to take one piece of advice from the talk, you should use more modern APIs like Custom Tabs, JavaScript Engine, or AndroidX's WebView variant.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7a816772-cc23-42e7-818e-4fd5153f7283", - "name": "Parth Padgaonkar" + "id": "6b808c1f-836d-4349-9585-a89355dfb8d0", + "name": "Balázs Gerlei" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, - "name": "Other" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false }, { - "id": 39640, - "name": "Hopper", + "id": 53982, + "name": "Nest", "sessions": [ { - "id": "520431", - "title": "Mobile Observability at scale", - "description": "Mobile applications have evolved in complexity and business significance, making it critical to closely monitor and maintain the apps availability and performance. Fast incident understanding and response in real time is crucial to keep trust both of your users and your stakeholders. \r\n\r\nIn this talk we will introduce Mobile Observability, explaining its basics and highlighting both commonalities and distinction with backend practices. Our focus will be on various metrics that can be collected within mobile apps, their purpose, instrumentation methods and alignment with Android Vitals or backend metrics.\r\n\r\nAndroid engineers will learn \r\n- How to set up Observability within their apps, establishing actionable metrics.\r\n- Site Reliability Engineering (SRE) practices and their application in the mobile world.\r\n- Selecting right tools and configuring alerting systems for appropriate response and actions.\r\n- Scaling Observability across 10+ teams.\r\nMaintenance, handling seasonality, legacy app versions or false positives.\r\n- Incident response best practices and techniques for quickly identifying the cause of issues.\r\n", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "731303", + "title": "Embracing 16 KB Page Sizes in Android - Boosting Performance", + "description": "Enhance app performance by adopting 16 KB page sizes introduced in Android 15, mandatory for Google Play uploads starting in 2025. In this session, I will cover the why and how behind 16 KB page size modifications through four parts: (1) Performance Boosts: Speed up app boot and launch times with 16 KB page size, (2) Impact Assessment: Evaluate how the 16 KB page size will affect your app, (3) Code Alignment: Align your code and binaries with the 16 KB page size requirement, and (4) Testing Strategies: Test and ensure your app is compliant with specific techniques. Get ahead of the curve by learning to adapt your app to the 16 KB page size requirement, ensuring superior performance and compliance ahead of the 2025 mandate.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3851a67a-7332-4247-a6cb-4e87efbf256c", - "name": "Josef Raska" + "id": "84335047-a7cd-437e-986f-1d6b6df31072", + "name": "Chrystian Vieyra Cortes" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185731, + "id": 264380, "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "536951", - "title": "Android Large Screen: how your app can power a workstation", - "description": "Learn how to make your Android Apps Large Screen Ready and Optimized.\r\n\r\nUnderstand what code changes are needed to get Android, Web and Flutter apps working well on desktop-style, multi-screen, or multi-window environments.\r\n\r\nApps that have been coded with accessibility (a11y) in mind from the beginning have a head-start in being Large Screen Optimized. Learn why a11y may be needed by anyone in certain situations, how it extends the reach of your apps, and how to bake-in accessible user experiences from the start.\r\n\r\nSee how Zebra Workstation Connect enables a Zebra mobile device to become a workstation/Point of Sale by utilising the Android Large Screen capabilities.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "711918", + "title": "Personalizing Accessibility", + "description": "What should you do when users' accessibility needs conflict? For example, some users prefer more color contrast, while others require less contrast. To give a few examples, some might need labels to accompany icons, and for some, having too much text can make the app experience worse. Some love graphs and others have difficulty understanding what they represent and would need the same data in written format.\r\n\r\nOne way to tackle this problem is to allow users to personalize their app experience. In this talk, I will discuss different ways of personalization, including the opportunities and drawbacks. I will also give some actionable examples of how to let your users personalize their experience. After listening to this talk, you'll leave with actionable knowledge about providing a more accessible experience for your users via personalization.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ca54f2c6-836e-41b2-ba24-78870a7c0384", - "name": "Benedict Kennedy" + "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", + "name": "Eeva-Jonna Panula" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185736, - "name": "Design" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185741, - "name": "UI/UX" + "id": 264365, + "name": "Accessibility" }, { - "id": 185744, - "name": "sponsor" + "id": 264368, + "name": "Techniques & Guides" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528097", - "title": "Beyond the Basics: Performance Monitoring and User Experience for Mobile App Growth", - "description": "When interacting with a mobile app that regularly crashes or freezes, 53% of users uninstalled the app, 37% stopped using it, and 28% looked for a replacement. Users are no longer forgiving mobile app mishaps and errors. Going beyond the basic metrics of understanding your user experience and diving deep into mobile performance is key to ensuring growth and positive user experiences. We will highlight which metrics and takeaways mobile teams should track to create a superior app experience that takes into account every user interaction. Find out why app insights, proactive issue detection, advanced debugging, and alert management capabilities should matter to you and the development of your mobile app.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "735538", + "title": "Unblocking The Main Thread: Solving ANRs and Frozen Frames", + "description": "In the realm of Android development, the main thread is our stage, but too often, it becomes a battleground where performance issues arise, leading to ANRs, frozen frames, and sluggish Uls. As we strive for excellence in user experience, understanding and optimizing the main thread becomes essential to prevent these common performance bottlenecks.\r\n\r\nWe have strategies and best practices for keeping the main thread uncluttered. We'll examine the root causes of performance issues and techniques for monitoring and improving main thread health as well as app performance.\r\n\r\nIn this talk, participants will walk away with practical knowledge on enhancing app performance by mastering the main thread. We'll share proven approaches to eliminate real-life ANRs and frozen frames to build apps that deliver butter smooth experience.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6519e3d1-ca80-46b3-99f5-0f77adfdc91f", - "name": "Sean Higgins" + "id": "9a7f5add-ce7a-49a3-ab3d-f3c4484453e8", + "name": "Sinan Kozak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185727, - "name": "Flutter" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185731, + "id": 264380, "name": "Tooling" }, { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264379, + "name": "Android Studio" }, { - "id": 185742, + "id": 264391, "name": "Firebase" - }, - { - "id": 185744, - "name": "sponsor" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529953", - "title": "Why can't my app open that file? A deep dive into the Android app sandbox", - "description": "Android keeps a close eye on what files your application can read and write. We call this the \"application sandbox\": a safe area for your code to run which prevents other apps from interfering with your data, and prevents you from interfering with other apps or the operating system\r\n\r\nIn this talk I will be looking at the rationale behind the sandbox, and how it works. I will look at file ownership, file access modes and the reasons for those \"Permission denied\" messages. I will dig down to find out exactly why an app can access only a well defined set of private files and shared storage. And I will explain\r\nhow SE Linux enforces all of this at at a deep level\r\n\r\nYou will be reassured that Android is a secure operating system. But, as you know there are always exceptions to the rules. So, in the final section of this talk I will show you how preinstalled system apps can crash though all the barriers\r\n", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "704275", + "title": "The state of code coverage for Kotlin", + "description": "Nowadays it’s not anymore a question whether we want to write tests or not. Writing unit tests is not “nice-to-have” it’s an essential part of our everyday job. While that’s already a great step forward, how can we be sure our tests are actually testing something. To check this we all use code coverage tools.\r\n\r\nIn this presentation you will learn how code coverage for Kotlin works, which tools we can use and what are the challenges and limitations of various approaches. You will learn the difference between line, instruction and condition coverage and how to read and interpret coverage results in Kotlin.\r\n\r\nIn the last part of this presentation we will cover the future for Kotlin Code Coverage, what is missing and what can be improved in the future.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5d3a7bdc-4d73-4985-91f2-a388978732a9", - "name": "Chris Simmonds" + "id": "eeb25beb-5d37-4fb7-acac-170357a1d89b", + "name": "Marharyta Nedzelska" + }, + { + "id": "d2ddc9e3-35b6-44a9-8daa-144d89cabaff", + "name": "Evgeny Mandrikov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185735, - "name": "Android" + "id": 264359, + "name": "Kotlin" }, { - "id": 185736, - "name": "Design" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529650", - "title": "Honest mistakes caused by shallow interpretation of SOLID", - "description": "Although coding, in essence, is a clear subject that normally doesn’t open space for too many points of view with clear rights and wrongs, it’s naive to think that the same problem can’t be correctly solved by different architectures or even opposed solutions.\r\n\r\nBy taking SOLID concepts as an argument to support their cases, less experience engineers tend to not take context and product requirements into account, risking the failure of the initiative for the sake of following good practices by the book.\r\n\r\nMany times it’s also common that just couple principles are taken into account, completely ignoring others that might even be more important given the scope of the problem.\r\n\r\nThis talk is about recognizing these destructive patterns on your team when designing a solution to help you to Built it Right, as long as you are Building the Right Thing.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "719034", + "title": "Navigation in a Multiplatform World: Choosing the Right Framework for your App", + "description": "Navigation in mobile, desktop, and web applications is such a fundamental part of how we structure our architecture. In order to both obtain functional clarity, and abstraction from platform level implementation.\r\n\r\nFor a long time, there have been options available specific to each platform, and even options part of the platform framework itself. Though it can be difficult to find the right option for platform-agnostic code, ensuring consistency. Some go one step further, providing an opinionated guide on how to architecture your application.\r\n\r\nIn this talk, I'll evaluate the options available, how they differ, and to what type of applications they are best suited. Including how to get started with them, and the best practice guidelines on how to get the most out of them, for your application.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b44c9f22-4d83-4246-bf67-bdd30af9bfef", - "name": "Haroldo Olivieri" + "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", + "name": "Ash Davies" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, - "name": "Other" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529128", - "title": "Navigation superpowers at your fingertips", - "description": "This talk will begin by the demonstration of a beautiful sample app built with Compose Mulitplatform and Appyx, complete with:\r\n\r\n- Shared element transitions\r\n- Scoped dependencies\r\n- Deep links that animate the app into a desired state\r\n- Gesture-controlled navigation\r\n\r\nWhile this is a wide range of topics, we’ll see how they belong together in one cohesive story, where they’re just different aspects of the same underlying theme: managing application state.\r\n\r\nIn this highly visual talk we’ll also see how using Appyx we can add the UI magic of custom transitions and gesture-control to our apps in no time.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "709201", + "title": "Passwordless Future !!", + "description": "Passwords: no one likes them.\r\n\r\nWith so many requirements—capital letters, numbers, symbols—and the challenge of remembering a strong one, passwords often become more of a hassle than a security measure. Even the most complex passwords can be vulnerable to hackers who can guess, steal, or trick you into revealing them.\r\n\r\nThankfully, a better solution exists, which we will explore in today’s session: Passkeys.\r\n\r\nWhat You'll Learn:\r\n- An introduction to Passkeys and the primary reasons for their implementation.\r\n- Integration of Passkeys and demos into Android apps using the credential API.\r\n- How users can sign in to apps and websites using biometric sensors (fingerprint or facial recognition), PINs, or patterns, creating a seamless sign-in experience that eliminates the need for usernames or passwords.\r\n- Best practices for implementing Passkeys in your applications to maximize both security and user convenience.\r\n- Case studies and examples of successful Passkey integration in real-world applications.\r\n\r\nTarget Audience:\r\nThis talk is ideal for mobile developers of all levels who are interested in:\r\n- Smoothing the login experience for their users across various platforms.\r\n- Enhancing security for their apps.\r\n- Staying ahead with modern authentication methods to improve user retention and satisfaction.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94bb39bf-8256-4633-adcd-54d621242cea", - "name": "Zsolt Kocsi" + "id": "0037b354-6bd4-46e3-b1e6-d84916eea0d6", + "name": "Ayushi Gupta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185710, - "name": "KMP" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185713, - "name": "Compose" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185739, - "name": "Libraries" + "id": 264384, + "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264385, + "name": "Design" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530595", - "title": "Sink or swim: proving ideas in production, fast", - "description": "It all began with a beer and a video call: a group of friends had an app idea and wanted to see whether it could work out. But going from idea to shipping something on the Play Store isn’t that easy. How do you organize work? What are the roles and tools to use? And most importantly, how to build a framework onto which you can iterate on multiple ideas, fast?\r\n\r\nThat’s what we had to figure out over the past six months as we launched multiple ideas into production. Join us in a retrospective of how we got from that famous beer to today. We’ll cover how an Android dev such as yourself can organize work to prioritize fast iteration, pick the right tools and technologies, automate the heck out of everything, figure out how to promote and grow the app, plan a business strategy, and understand if it is sustainable.\r\n\r\nBy attending this session, you’ll find out what worked out for us and what didn’t in this process, and how to avoid some mistakes we made. If you’ve ever had the itch to publish an app — by yourself or with some friends — come along to find out what’s ahead of you!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "2823786d-8670-4816-801e-9579b16c87cc", + "title": "Coming Soon!", + "description": null, + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 53982, + "room": "Nest", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 54703, + "name": "Chromecast", + "sessions": [ + { + "id": "757520", + "title": "Roundtable: Keeping Up With Android – How to Stay Relevant", + "description": "\r\nIn the fast-paced world of Android development, staying current and continually honing your skills is essential for career growth and relevance. With constant advancements, new tools, libraries and APIs emerging…it can be easy to feel overwhelmed, have FOMO and feel like you are being left behind. In this roundtable we’ll discuss:\r\n - How do you prioritize what new technologies or trends to focus on?\r\n - What are some effective strategies or habits you use to continually hone your skills while balancing a busy work schedule?\r\n - How do you assess whether a new technology is worth investing time and energy into learning?\r\n - Can you share any resources, communities, or platforms that have been particularly helpful in staying up to date?\r\n - How do you prevent burnout?\r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "83d33e86-8cca-44a8-b0dc-2994332ef8e8", - "name": "Ivan Morgillo" - }, - { - "id": "8fc4211d-38ef-4661-acee-16b4ea9cc800", - "name": "Aurelio Laudiero" - }, - { - "id": "f897009d-854d-4403-ab47-44c29d8edce1", - "name": "Daniele Bonaldo" + "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", + "name": "Neil Hutchison" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 - }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185733, - "name": "AI/ML" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185742, - "name": "Firebase" - } - ], + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - } - ], - "hasOnlyPlenumSessions": false - }, - { - "id": 39641, - "name": "Booth", - "sessions": [ + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": "530249", - "title": "Creating Engaging Android TV and Fire TV Apps with Native and Cross-Platform Tools", - "description": "Ready to launch your app or game on the big screen? Whether you are targeting Google Android TV or Amazon Fire TV, this talk is your guide to creating great “10 foot UIs” for customers watching television on their couch!\r\nIn this session, we’ll dive into the best practices for TV app development. Get ready to explore the key design principles, development strategies, and performance optimizations. We’ll cover everything you need to know, from creating beautiful UIs with Kotlin and Jetpack Compose for TV, React Native or Flutter, to optimizing remote control inputs to ensure a seamless navigation experience.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "760569", + "title": "Roundtable: KMP & CMP - Yes we can!...but....", + "description": "KMP with native UI and KMP with CMP are both amazing technically. However, there is no guidance for how these technologies should be applied to production situations. All new tech goes through a maturing phase.\r\n\r\nJoin this roundtable to discuss the promise and pitfalls of KMP and CMP and how the community can deliver on the amazing potential.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "106dc281-fe8b-40ac-b16b-240832b27b47", - "name": "Giovanni Laquidara" + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185736, - "name": "Design" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "74a094cd-2f41-4906-8427-7c720694b385", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "00e42524-0d24-448a-b3e6-8dc57352c3d7", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "f431dc83-3136-4892-8c38-b289aac3710f", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + { + "id": "e4ce5c39-ca56-4b02-854f-ad4c89ea88da", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false }, { - "id": "530186", - "title": "Coding for Good: Achieving social change with an app", - "description": "As coders, we have an enviable ability to create platforms and apps that bring people together. The world is not short of challenges right now, especially climate change, but we will only have a real impact on the world if we can help people unite and organise to pressure those with power and influence.\r\n\r\nChris and his husband quickly developed an app ten years ago to allow ordinary people to lobby the normally inaccessible and unelected House of Lords to pass the Same Sex Marriage Bill. It unexpectedly took off, with 15,000 emails sent in the space of two weeks, helping the Bill overwhelmingly succeed in the normally-socially-conservative upper House of Parliament in the UK.\r\n\r\nHe'll talk here about how he did it, and how you - an individual with a computer and knowledge of how to knock an app together - can use this skill to give a voice to the voiceless, and to potentially change or even save the world.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "4e3e8d41-d83c-4f91-8742-bf75d4b54436", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + } + ], + "hasOnlyPlenumSessions": false + }, + { + "id": 54706, + "name": "Ask Android", + "sessions": [ + { + "id": "764372", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:00:00", + "endsAt": "2024-11-01T10:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f53882b1-df66-4d6c-9ec1-888badcdd224", - "name": "Chris Ward" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "511354", - "title": "Refactoring and Test Fakes: Crafting Resilient Code with Confidence", - "description": "Crafting resilient code is one of the most important things we do as software developers, but it's much easier said than done! Building with confidence requires an appropriate test harness and automated safeguards to ensure your software is robust.\r\n\r\nIn most real world scenarios, we don't have the luxury of working with a green field project, so it can be difficult to apply best practices whilst maintaining legacy code. How then can we refactor, and effectively utilise test fakes appropriately?\r\n\r\nIn this talk, I'll discuss the best approaches for using test fakes, mocks, stubs, and what are the pros and cons for each. How we can avoid writing brittle tests, slowing down development, and build scalable apps that can stand the test of time.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "764376", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", - "name": "Ash Davies" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "496982", - "title": "Improving Video Playback with ExoPlayer", - "description": "Video has become an integral part of our life, and we are witnessing a significant rise in the integration of video content within Android apps. Sadly, there isn't much information out there about videos on Android. I've personally had a hard time finding practical ways to make videos better.\r\n\r\nIn this talk, my primary focus will be on sharing practical approaches with ExoPlayer that go beyond what is documented: We'll discuss the common problems with playbacks, solutions and will find a performant approach to use ExoPlayer together with Jetpack Compose.\r\n\r\nI'll share everything that I learned practically during the last 8 months:\r\n- Video basics: Media3, ExoPlayer and how it works\r\n- Bandwidth and it's role\r\n- Adaptive streamable protocols (HLS/Dash) vs custom ABR implementation\r\n- Caching and Video Prefetching\r\n- Initial video resolution improvements\r\n- Time to the first frame improvements\r\n- Performance with Jetpack Compose\r\n- Architectural Approaches\r\n\r\nEverything that will be mentioned is validated through real production scenarios and confirmed an efficiency by A/B tests. ", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "764378", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:00:00", + "endsAt": "2024-11-01T11:15:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "61cc0eba-ac00-414b-9cf7-5525040a4bdd", - "name": "Alexey Bykov" + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "391191b2-1d5c-4784-875b-00edf4d065cc", + "name": "Chris Assigbe" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + }, + { + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "522982", - "title": "Staying Relevant", - "description": "When working on legacy projects and slow to change codebases, it can feel like you are being left behind, while others are actively learning and using the latest technologies.\r\n\r\nI want to share my approach with you, to help keep on top of things and remove any FOMO.\r\n\r\nThe inspiration for this talk comes from working on a legacy project for the last couple of years.\r\nI’ve felt like I was becoming worse at my job, that I’m becoming outdated and falling behind. I want to help people not feel the way I felt.\r\nBeing at a conference seeing all the latest and greatest things can reinforce those feelings, so I will help bring everything back into perspective.\r\n\r\nSpecifically, I will outline a series of strategies you can follow, from day to day activities to thinking in the years of your career, and a specific set of recommendations for our current state in 2023.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "764381", + "title": "Ask Android! -GenAI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", - "name": "Neil Hutchison" + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185718, - "name": "Techniques & Guides" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530295", - "title": "Now smile (also in 3D)! Exploring AR, ML and Camera related APIs on Android", - "description": "When it comes to Camera APIs, Android has come a long way since its start, which is good, not only for the platform consistency above Lollipop but also because now we can increase its potential by using AR and ML APIs integrations, such as AR Core, ML Kit and MediaPipe. \r\n\r\nHowever, it can bit confusing if you're not familiar with the existing available APIs, which one to use for specific scenarios and how to approach Camera2, and CameraX in general.\r\n\r\nDuring this talk, you will learn the basics of camera APIs on Android and how to extend them to different use cases to enhance your app use experience!", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "764391", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:55:00", + "endsAt": "2024-11-01T12:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "226909f8-7c0a-43bc-b296-a3f2c369b9a3", - "name": "Walmyr Carvalho" + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185728, - "name": "Modern Android Development" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "529420", - "title": "Unlocking Unity for Android Developers: Blending Android and Unity in Harmony", - "description": "We've all become accustomed to working on Android projects as part of our daily work. But have you ever thought about how you could seamlessly adapt your existing solutions to play nice with other engines like Unity? Or perhaps you’re considering creating an entirely new library to incorporate functionalities that aren't readily available in the core Unity. \r\n\r\nSimilarly, Unity paves the way for hybrid Android development, allowing you to embed Unity-created elements, mini-games and augmented reality directly into your Android apps.\r\n\r\nIn this talk, we're delving into the Unity landscape from an Android developer's perspective. We'll unravel the intricate interplay between Unity scripts and Android code, gaining insights into how IL2CPP and Mono scripting backends operate to seamlessly unite C# and Kotlin.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764396", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bb2df6d6-edb0-416a-85fc-83b062c6c10c", - "name": "Yevhen Pekutovskyi" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "id": "764397", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:35:00", + "endsAt": "2024-11-01T13:50:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - }, + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185735, - "name": "Android" + "id": 306233, + "name": "Office Hours" } ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" - } - ], - "hasOnlyPlenumSessions": false - }, - { - "id": 39642, - "name": "Lamarr", - "sessions": [ + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, { - "id": "518711", - "title": "Hitting a Gold Mine: How to Set the Gold Standard With Golden Tests", - "description": "Golden tests generate screenshots of Flutter widgets and compare them against reference images. They are a powerful tool to protect from visual regressions and improve the quality of your apps. \r\n\r\nIn this talk, we’ll go beyond simple examples and take a look at how we can make golden tests part of a development workflow in a bigger Flutter project. In a Flutter project developed by LeanCode for a client from the banking sector, we decided to experiment with golden tests from the very beginning, and they turned out to be extremely effective. \r\n\r\nDuring the talk, we’ll see what are some good practices and antipatterns for golden tests, what role they can play in the code review process, and take a broader perspective on who can be involved in golden tests (not only developers!). We’ll also consider using golden tests in more advanced testing scenarios for things like responsiveness, accessibility, localization, and others.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "764398", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "15d3a177-f21f-4b7b-8f42-3f7694477f21", - "name": "Marcin Chudy" + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "518103", - "title": "Nested Navigation in flutter web", - "description": "In Flutter web, developing Nested navigation is always a critical topic. \r\nEvery project has different needs, Some project needs Bottom navigation some need Sidebar navigation, and some need some sidebar with nested navigation. Too many combinations and complications!! it’s a hard decision to decide on an approach similar to the packages. Which package should we use and why? In this talk, I am sharing my story about Go Router. How Shell route is a change maker and solved many problems. Let’s learn more about the Shell route. and its different use cases. Make Flutter web navigation simple and easy.\r\n", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "764399", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:30:00", + "endsAt": "2024-11-01T14:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "990dea5b-f32e-4260-8551-967e2597bee2", - "name": "Renuka Kelkar" + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "521242", - "title": "How not to ship an app. Lessons and experiences shipping bugs to production.", - "description": "Have you ever experienced the anxiety of shipping a mobile app to production, with your fingers crossed that nothing is going to go wrong? If so then this is the talk for you, and if it is not, maybe you will learn something new.\r\n\r\nOver the past 2+ years, we have shipped a lot of bugs in our production app, with this talk I aim to condense the most important learnings we had and the actions we took to mitigate those. Openly sharing our experiences will not only help you avoid similar pitfalls but also foster a culture of learning and improvement within the Flutter community.\r\n\r\nWhether you are an aspiring developer, an experienced professional, or a team lead, this talk will offer valuable insights and practical tips to improve your app's quality.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "764400", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "758dff56-ba98-4d4d-ab60-87472b2b4846", - "name": "Efthymios Sarmpanis" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530144", - "title": "Deep Dive into Flutter Animations: Crafting Dynamic and Engaging UIs", - "description": "Animations are more than just visual flair; they breathe life into mobile applications, making them feel responsive and intuitive. In this session, we'll embark on a deep dive into Flutter's animation toolkit. From the foundational principles of animating widgets to advanced techniques like gesture-driven transitions and physics-based movements, attendees will discover how to elevate their app's user experience to new heights. Through hands-on demonstrations, learn to harness the full potential of Flutter's animation capabilities and craft truly dynamic and engaging UIs.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "764404", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:30:00", + "endsAt": "2024-11-01T15:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6284f70b-bae3-4b49-a672-52b5b7ca5124", - "name": "Josteve Adekanbi" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "528773", - "title": "Rolling in the deep(link) - take a deep dive into Flutter navigation", - "description": "Deep linking is an essential feature in mobile apps that allows users to access specific pages or sections of an app from an external source like a link or notification. \r\n\r\nIn this talk, I will cover everything you need to know about handling deep links in Flutter mobile apps. \r\n\r\nWe'll start by exploring an independent solution for handling deep links in Flutter, followed by discussing third-party solutions that offer additional features such as dynamic link generation and deferred deep links. We'll also examine the benefits and limitations of using these solutions and how to implement them in a Flutter app. \r\n\r\nThroughout the talk, we'll discuss common issues and solutions when handling deep links in Flutter apps. \r\n\r\nAttendees will leave with a comprehensive understanding of deep linking in Flutter and the knowledge to implement deep linking solutions in their mobile apps.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "764406", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "d63cf645-9a0e-434b-bebe-4a3825755cb5", - "name": "Alicja Ogonowska" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "527829", - "title": "Seamless Interoperability: Harnessing JNIGen and FFIGen", - "description": "This talk dives into the world of seamless interoperability, unveiling the power of JNIGen and FFIGen in enabling direct access to native methods from Dart code. Unlike the current approach of using Platform channels in Flutter, JNIGen, and FFIGen allows us to achieve effortless access to native methods from Dart code. By eliminating the need for manual method handling and channel management, these tools revolutionize the development process. With practical examples and performance comparison, attendees will discover how JNIGen simplifies integration with Android libraries and JAVA classes, while FFIGen streamlines access to Swift and Objective-C libraries. ", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "764408", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:25:00", + "endsAt": "2024-11-01T16:40:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76ce51b1-7dd4-4858-98de-0b51b3c9abcb", - "name": "Akanksha Singh" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" }, { - "id": "d9cf6863-3524-4461-b5d2-ee91cc1595f2", - "name": "Kendi J" + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { - "id": "530074", - "title": "Bring the power of Postgres to your Flutter app with Supabase", - "description": "Supabase is an open-source Firebase alternative centered around Postgres database, one of the world’s most popular databases known for its extensibility, performance, and data integrity. You can create a fresh Supabase project with just a few clicks, and you will gain access to a fully managed Postgres database with serverless APIs, real-time database subscription, auth, file storage, and edge functions. With its Flutter SDK, developers can access the Supabase database securely and efficiently, helping them build complex apps in a matter of days. \r\n\r\nIn this session, I will give you an overview of what Supabase is, and how it is helping developers build scalable applications efficiently. We will also look at a few examples where advanced data types of Postgres, such as geo data, or range data, can help you build complex Flutter applications efficiently. \r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764409", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "31f0ce3c-630f-4146-a6f5-9e6c7f5cad62", - "name": "Tyler Shukert" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185724, - "name": "API" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "hasOnlyPlenumSessions": false @@ -9591,3288 +12253,4057 @@ ], "timeSlots": [ { - "slotStart": "08:00:00", + "slotStart": "08:20:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "e167a3bd-c503-4ed8-b56f-ec30897857ee", + "id": "8586de07-b3e8-4ebe-8212-f54e5f817f7d", "title": "Registration & Check-In", "description": null, - "startsAt": "2023-10-27T08:00:00", - "endsAt": "2023-10-27T09:00:00", + "startsAt": "2024-11-01T08:20:00", + "endsAt": "2024-11-01T09:20:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + } + ] + }, + { + "slotStart": "09:20:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "759067", + "title": "The Future of Android, Kotlin, and Everything", + "description": "Modern mobile and Android are a touch over 15 years old and Droidcon London 2024 is also in its 15th year. \r\n\r\nSo, it’s time to look forward.\r\n\r\nTech trends come and go. Some burn hot, then disappear. Some become essential. The web is essential. Mobile is essential. Whatever happens with mobile technology, the idea that mobile won’t be as, or more essential, 15 years from now is nonsense.\r\n\r\nHow mobile is developed and delivered is a different question altogether. This will be a talk about near-term tech possibilities, but not what mobile might look like in 15 years. I could speculate, and I'd be wrong. It will be a talk about how this community can shape that future, starting now.\r\n\r\nWhile the tech hype machine has hopped from one shiny concept to another, neither the web nor mobile has remained static. Far from it. We’re in a moment of change and opportunity. A very special moment for this community specifically. But the community is necessary to take advantage of it.", + "startsAt": "2024-11-01T09:20:00", + "endsAt": "2024-11-01T10:00:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264357, + "name": "Keynote" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 0 + } + ] + }, + { + "slotStart": "10:00:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "6876a148-9098-4d59-809f-995afbb7b110", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T10:00:00", + "endsAt": "2024-11-01T10:20:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764372", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:00:00", + "endsAt": "2024-11-01T10:20:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 6 } ] }, { - "slotStart": "09:00:00", + "slotStart": "10:20:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "538663", - "title": "Fireside Chat with Googlers", - "description": null, - "startsAt": "2023-10-27T09:00:00", - "endsAt": "2023-10-27T09:50:00", + "id": "733921", + "title": "Effective App Monitoring in Production", + "description": "Your feature is complete and ready to be shipped to production. Now you can forget about it completely and move onto the next thing, right? Actually the fun begins when your feature reaches your users. Is your feature healthy? Are your users seeing any issues? Which ones? How many?\r\nIn this session, we’ll discuss how to monitor Android apps in production to ensure they stand resilient and ready for the challenges of the real world. \r\n\r\nIn this session, you’ll learn how to design and report relevant events that matter to you and your team. If you are introducing monitoring in your application, you will learn how to design a tracking strategy to effectively monitor the application health and debug any issue users experience. If you already have monitoring in place, you will see techniques on how to get visibility into the data through the definition of KPIs (Key Performance Indicators) and dashboards. We will also provide examples of common problems teams face when implementing constant monitoring like knowledge sharing, alerting, and escalating issues to other teams.\r\n\r\nYou’ll see from real world examples how to track, detect and address production issues before they escalate.\r\n\r\nLevel up your app observability, delight your users and deliver with confidence with these tips.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, - "isPlenumSession": true, + "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "18c45d50-3e38-4933-a55b-8a833f850115", + "name": "Alejandra Stamato" + }, + { + "id": "084a1449-47c9-488c-b5b6-5b8534c45a23", + "name": "Mauro Frezza" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185706, - "name": "panel" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 - } - ] - }, - { - "slotStart": "09:50:00", - "rooms": [ + }, { - "id": 36716, - "name": "Lovelace", + "id": 49271, + "name": "Glass", "session": { - "id": "84ad59e0-d9b4-46a9-a22a-0dfdd70342e2", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T09:50:00", - "endsAt": "2023-10-27T10:15:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "id": "696374", + "title": "AndroidX Lifecycle's Path to Multiplatform", + "description": "We’ve recently converted the AndroidX Lifecycle libraries (ViewModel, Lifecycle Runtime, and Compose support) to Kotlin multi-platform (KMP). Join this session to learn more about how this process went, what the real-world challenges of maintaining API backward compatibility are, what lessons we learned from working around KMP limitations, and insights to guide you in migrating your own libraries to KMP.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 - } - ] - }, - { - "slotStart": "10:15:00", - "rooms": [ + "index": 1 + }, { - "id": 36716, - "name": "Lovelace", + "id": 49272, + "name": "Things", "session": { - "id": "524652", - "title": "Peeling Back the Layers: Unmasking the UI-nknown!", - "description": "How much do you know about the UI layer and its best practices? What's the preferred way to produce UiState? How to consume it? Should you use MVVM or MVI? What about handling UI events? Where to hoist a particular state? What types of state holders can you have in your app? Should the ViewModel contain all the logic that comes from the UI? What are its limits?\r\n\r\nAll these questions and more about the UI layer will be answered in the talk. Level up your UI layer knowledge and make it a well-known area. We'll use real code snippets to show how to apply the recommended practices in an Android app.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "696208", + "title": "Deep Dive into the Compose Compiler", + "description": "Have you ever wondered how the slot table is implemented in Compose? How are groups generated? What's the magic behind skipping, and how can you peek inside a slot table? The Slot table implementation was updated recently for performance. This talk takes a deep dive into the Jetpack Compose compiler internals, with a special focus on answering these questions and exploring the implementation details of the slot table. We'll go beyond the basics, uncovering how the compiler is structured and tested. This knowledge will empower you to understand, contribute to, or even build your own tools on top of it. Whether you're a curious developer or aspiring plugin contributor, get ready to fully understand the Jetpack Compose Compiler.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b4cf78c9-623b-4b8f-b676-097fb7802668", - "name": "Manuel Vivo" + "id": "a79f3682-df5c-40e4-8aa8-1b6eaa7bdeec", + "name": "Mohit Sarveiya" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185713, - "name": "Compose" + "id": 264361, + "name": "KMP" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264364, + "name": "Compose" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264380, + "name": "Tooling" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 2 + }, + { + "id": 53981, + "name": "Stadia", + "session": { + "id": "720892", + "title": "Seamless mobile real-time communication with WebRTC", + "description": "The WebRTC project allows developers to build smooth voice and video communication solutions by facilitating the transmission of data between peers.\r\nDuring this talk, we will explore the details of the WebRTC project, including its design principles, functionalities, and how it transforms communications for many web and mobile apps. \r\nFinally, we will create a simple mobile application in Kotlin Multiplatform using WebRTC so you will want to start a business and become a billionaire. \r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "4674e36f-50f8-4ffb-a486-679d730c759e", + "name": "Renaud Mathieu" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264388, + "name": "Libraries" + } + ], + "sort": 4 + } + ], + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 3 + }, + { + "id": 53982, + "name": "Nest", + "session": { + "id": "731303", + "title": "Embracing 16 KB Page Sizes in Android - Boosting Performance", + "description": "Enhance app performance by adopting 16 KB page sizes introduced in Android 15, mandatory for Google Play uploads starting in 2025. In this session, I will cover the why and how behind 16 KB page size modifications through four parts: (1) Performance Boosts: Speed up app boot and launch times with 16 KB page size, (2) Impact Assessment: Evaluate how the 16 KB page size will affect your app, (3) Code Alignment: Align your code and binaries with the 16 KB page size requirement, and (4) Testing Strategies: Test and ensure your app is compliant with specific techniques. Get ahead of the curve by learning to adapt your app to the 16 KB page size requirement, ensuring superior performance and compliance ahead of the 2025 mandate.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "84335047-a7cd-437e-986f-1d6b6df31072", + "name": "Chrystian Vieyra Cortes" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264380, + "name": "Tooling" + } + ], + "sort": 4 + } + ], + "roomId": 53982, + "room": "Nest", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 4 + }, + { + "id": 54703, + "name": "Chromecast", + "session": { + "id": "757520", + "title": "Roundtable: Keeping Up With Android – How to Stay Relevant", + "description": "\r\nIn the fast-paced world of Android development, staying current and continually honing your skills is essential for career growth and relevance. With constant advancements, new tools, libraries and APIs emerging…it can be easy to feel overwhelmed, have FOMO and feel like you are being left behind. In this roundtable we’ll discuss:\r\n - How do you prioritize what new technologies or trends to focus on?\r\n - What are some effective strategies or habits you use to continually hone your skills while balancing a busy work schedule?\r\n - How do you assess whether a new technology is worth investing time and energy into learning?\r\n - Can you share any resources, communities, or platforms that have been particularly helpful in staying up to date?\r\n - How do you prevent burnout?\r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", + "name": "Neil Hutchison" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 303052, + "name": "Roundtable Discussion" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 5 }, { - "id": 36717, - "name": "Hamilton", + "id": 54706, + "name": "Ask Android", "session": { - "id": "529751", - "title": "Performance in Compose: Lessons learned from Lazy layouts", - "description": "The Compose team focused on performance extensively over the last few years. Many of the findings are documented and shared in different forms, but optimization continues to be one of the arcane arts of Compose. \r\n\r\nIn this talk, Andrei shares his own experience on optimizing Lazy layouts, focusing on the most notable tips that might help your application performance. He will provide a detailed description of Compose performance characteristics, summarizing a year long team effort to improve (re-)composition performance. Aside from the theory, you’ll also get practical tips about improving your composables derived from the experience of the Compose team.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "764376", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1e19e15d-18c9-4cd9-82b2-280120a8c8a6", - "name": "Andrei Shikov" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 6 + } + ] + }, + { + "slotStart": "11:00:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "cfcf5ef1-b7f7-46c2-82e1-ff3002c842e1", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T11:00:00", + "endsAt": "2024-11-01T11:15:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 }, { - "id": 36718, - "name": "Liskov", + "id": 54706, + "name": "Ask Android", "session": { - "id": "527210", - "title": "Bluetooth Magic, first level", - "description": "We use it all the time, but what actually is Bluetooth and how do we add it to our apps? What is the magic behind it? \r\nJoin me in this talk to go over all the components required for assembling your own Bluetooth feature, how to discover devices, how to transfer data among them and what different options do we have. All of this grounded with examples, tips and tricks coming from my own experience with it.\r\nAfter this talk, you should have the base knowledge to add Bluetooth to your apps. Because, sometimes, we all, need a little bit of... magic!", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "764378", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:00:00", + "endsAt": "2024-11-01T11:15:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", - "name": "Nicole Terc" + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "391191b2-1d5c-4784-875b-00edf4d065cc", + "name": "Chris Assigbe" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + }, + { + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185719, - "name": "Other" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 - }, + "index": 6 + } + ] + }, + { + "slotStart": "11:15:00", + "rooms": [ { - "id": 39640, - "name": "Hopper", + "id": 49270, + "name": "Hangouts", "session": { - "id": "520431", - "title": "Mobile Observability at scale", - "description": "Mobile applications have evolved in complexity and business significance, making it critical to closely monitor and maintain the apps availability and performance. Fast incident understanding and response in real time is crucial to keep trust both of your users and your stakeholders. \r\n\r\nIn this talk we will introduce Mobile Observability, explaining its basics and highlighting both commonalities and distinction with backend practices. Our focus will be on various metrics that can be collected within mobile apps, their purpose, instrumentation methods and alignment with Android Vitals or backend metrics.\r\n\r\nAndroid engineers will learn \r\n- How to set up Observability within their apps, establishing actionable metrics.\r\n- Site Reliability Engineering (SRE) practices and their application in the mobile world.\r\n- Selecting right tools and configuring alerting systems for appropriate response and actions.\r\n- Scaling Observability across 10+ teams.\r\nMaintenance, handling seasonality, legacy app versions or false positives.\r\n- Incident response best practices and techniques for quickly identifying the cause of issues.\r\n", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "736477", + "title": "Creating a Custom Compose Layout, Step-by-Step", + "description": "Building a custom layout in Jetpack Compose may seem intimidating, but it does not have to be. With an understanding of the tools available for custom layout, the phases of composition, and best practices for state management, anyone can create a powerful, beautiful custom layout.\r\n\r\nIn this talk, we will build a sophisticated schedule component step-by-step. This composable will include multiple display modes, theme-like encapsulation of styling and behaviors, scoped modifiers for easy customization, and even some animation.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3851a67a-7332-4247-a6cb-4e87efbf256c", - "name": "Josef Raska" + "id": "2d41d3fb-3321-4b47-a401-ae84a5de2423", + "name": "Huyen Tue Dao" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" + "id": 264358, + "name": "Jetpack Compose" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 0 }, { - "id": 39641, - "name": "Booth", + "id": 49271, + "name": "Glass", "session": { - "id": "530249", - "title": "Creating Engaging Android TV and Fire TV Apps with Native and Cross-Platform Tools", - "description": "Ready to launch your app or game on the big screen? Whether you are targeting Google Android TV or Amazon Fire TV, this talk is your guide to creating great “10 foot UIs” for customers watching television on their couch!\r\nIn this session, we’ll dive into the best practices for TV app development. Get ready to explore the key design principles, development strategies, and performance optimizations. We’ll cover everything you need to know, from creating beautiful UIs with Kotlin and Jetpack Compose for TV, React Native or Flutter, to optimizing remote control inputs to ensure a seamless navigation experience.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "747072", + "title": "Scalable Testing Strategies", + "description": "It's very easy to have too many tests and lose track of what kind of feedback each one brings. Moreover, testing on multiple devices can get expensive, both in terms of CI cost and lost productivity waiting for tests to pass. In this talk we'll talk about how the testing pyramid has evolved because of new requirements and features that need to be tested, such as multiple form factors, new configuration changes, Jetpack Compose, screenshot testing, etc.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "106dc281-fe8b-40ac-b16b-240832b27b47", - "name": "Giovanni Laquidara" + "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", + "name": "Jose Alcérreca" + }, + { + "id": "532dc16d-b69d-4f68-aa49-fe9878b0b52f", + "name": "Adarsh Fernando" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185728, - "name": "Modern Android Development" + "id": 264394, + "name": "CI/CD" }, { - "id": 185736, - "name": "Design" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 + "index": 1 }, { - "id": 39642, - "name": "Lamarr", + "id": 49272, + "name": "Things", "session": { - "id": "518711", - "title": "Hitting a Gold Mine: How to Set the Gold Standard With Golden Tests", - "description": "Golden tests generate screenshots of Flutter widgets and compare them against reference images. They are a powerful tool to protect from visual regressions and improve the quality of your apps. \r\n\r\nIn this talk, we’ll go beyond simple examples and take a look at how we can make golden tests part of a development workflow in a bigger Flutter project. In a Flutter project developed by LeanCode for a client from the banking sector, we decided to experiment with golden tests from the very beginning, and they turned out to be extremely effective. \r\n\r\nDuring the talk, we’ll see what are some good practices and antipatterns for golden tests, what role they can play in the code review process, and take a broader perspective on who can be involved in golden tests (not only developers!). We’ll also consider using golden tests in more advanced testing scenarios for things like responsiveness, accessibility, localization, and others.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "735816", + "title": "Developing Trusted Applications for Trusted Execution Environments and vTEEs", + "description": "In today's mobile-first world, security is paramount. Join us in this engaging session where we introduce vTEE, a groundbreaking concept designed to embed advanced security into mobile applications. This session will demonstrate how vTEE allows developers to create their own Trusted Execution Environments (TEEs) directly within their apps, ensuring that sensitive data remains protected. Additionally, learn how our global award-winning open-source project, jCardSim, simplifies the prototyping and development of trusted applications, accelerating your journey towards secure, scalable, and innovative app experiences. Get ready to supercharge your app security with ease and flexibility!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "15d3a177-f21f-4b7b-8f42-3f7694477f21", - "name": "Marcin Chudy" + "id": "d7e7d278-a645-42c5-b337-c793a5b5bd79", + "name": "Mikhail Dudarev" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" + }, + { + "id": 264381, + "name": "Cross-Platform" }, { - "id": 185727, - "name": "Flutter" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "10:55:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "2b8aba4e-4ee3-47e4-9972-29c3166c1c3a", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T10:55:00", - "endsAt": "2023-10-27T11:10:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 - } - ] - }, - { - "slotStart": "11:10:00", - "rooms": [ + "index": 2 + }, { - "id": 36716, - "name": "Lovelace", + "id": 53981, + "name": "Stadia", "session": { - "id": "538655", - "title": "Practical Magic with Animations in Compose", - "description": "Are you constantly in awe of the animations your designer creates but have no idea how to implement them? Or maybe you want to keep your users engaged by adding delightful little treats in your app... There are a few key principles that unlock a wide range of different animations, from basic to advanced.\r\n\r\nWe will go through some practical examples of how to implement animations in Jetpack Compose, such as working with gestures to control animations and how to do state based animations. Join us to learn how to think about implementing any animation in a step-by-step way. ", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "722874", + "title": "From realtime CameraX filters to General Purpose GPU computing in simple steps", + "description": "Did you ever think that that the computational complexity of image processing is comparable to that of AI training, and both tasks use GPUs?\r\n\r\nModern phones have powerful GPUs that are typically used for photo, graphics and image processing which naturally brings us to OpenGL and Vulkan C++ APIs.\r\n\r\nLuckily, on Android we have OpenGL Java bindings, so we don't need to dive into C++. And combined with the CameraX effects API, we can build advanced filters for the camera. What is more, mobile GPUs are also not limited to these types of tasks and can be used for general purpose computing, even the ones completely unrelated to graphics.\r\n\r\nIn this session I will show you how to set up and write your own shaders for real-time image processing with CameraX. I will also show you how to make a step forward and use shaders for general purpose GPU computing, so that you can take advantage of the GPU power.\r\n\r\nCome to this talk and learn how to harness the computing power of your phone's GPU!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "892ec2dc-5b2e-45fb-9393-ff1407ebebe7", + "name": "Konstantin Zolotov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 3 }, { - "id": 36717, - "name": "Hamilton", + "id": 53982, + "name": "Nest", "session": { - "id": "520549", - "title": "Composing an API the *right* way", - "description": "Everyone who writes code using Jetpack Compose designs Composable functions and components all the time. In this talk, we'll take a look at some highlights from the official guidelines around designing Compose APIs, to see how we can do a better job of building with Compose.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "711918", + "title": "Personalizing Accessibility", + "description": "What should you do when users' accessibility needs conflict? For example, some users prefer more color contrast, while others require less contrast. To give a few examples, some might need labels to accompany icons, and for some, having too much text can make the app experience worse. Some love graphs and others have difficulty understanding what they represent and would need the same data in written format.\r\n\r\nOne way to tackle this problem is to allow users to personalize their app experience. In this talk, I will discuss different ways of personalization, including the opportunities and drawbacks. I will also give some actionable examples of how to let your users personalize their experience. After listening to this talk, you'll leave with actionable knowledge about providing a more accessible experience for your users via personalization.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", - "name": "Márton Braun" + "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", + "name": "Eeva-Jonna Panula" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, - "name": "Kotlin" + "id": 264365, + "name": "Accessibility" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 4 }, { - "id": 36718, - "name": "Liskov", + "id": 54703, + "name": "Chromecast", "session": { - "id": "529068", - "title": "Demystifying Dagger", - "description": "Dagger gets a bad rap for being \"difficult to understand\", or \"overly complicated\", or \"black magic\". In reality, Dagger generates pretty straight-forward, elegant wiring code similar to how we would likely write it manually and, I believe, it's the complexity of large scale apps that makes it complex.\r\n\r\nIn this talk we'll look at different types of injection that Dagger supports, and the code it generates to do so, to get an understanding of what it's doing and how it works. We'll then dive into Components and Subcomponents to see how Dagger wires together our dependencies into a graph. Finally, we'll look at how Dagger can be extended using Anvil and other tools.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "760569", + "title": "Roundtable: KMP & CMP - Yes we can!...but....", + "description": "KMP with native UI and KMP with CMP are both amazing technically. However, there is no guidance for how these technologies should be applied to production situations. All new tech goes through a maturing phase.\r\n\r\nJoin this roundtable to discuss the promise and pitfalls of KMP and CMP and how the community can deliver on the amazing potential.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f1275dc9-492b-4ab1-b31b-3e19bccde628", - "name": "Ryan Harter" + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185738, - "name": "Dagger" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 5 }, { - "id": 39640, - "name": "Hopper", + "id": 54706, + "name": "Ask Android", "session": { - "id": "536951", - "title": "Android Large Screen: how your app can power a workstation", - "description": "Learn how to make your Android Apps Large Screen Ready and Optimized.\r\n\r\nUnderstand what code changes are needed to get Android, Web and Flutter apps working well on desktop-style, multi-screen, or multi-window environments.\r\n\r\nApps that have been coded with accessibility (a11y) in mind from the beginning have a head-start in being Large Screen Optimized. Learn why a11y may be needed by anyone in certain situations, how it extends the reach of your apps, and how to bake-in accessible user experiences from the start.\r\n\r\nSee how Zebra Workstation Connect enables a Zebra mobile device to become a workstation/Point of Sale by utilising the Android Large Screen capabilities.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "764381", + "title": "Ask Android! -GenAI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ca54f2c6-836e-41b2-ba24-78870a7c0384", - "name": "Benedict Kennedy" + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185736, - "name": "Design" - }, - { - "id": 185741, - "name": "UI/UX" - }, - { - "id": 185744, - "name": "sponsor" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 6 + } + ] + }, + { + "slotStart": "11:55:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "2e322db2-0dcf-458b-9dc2-c6a62337a8e4", + "title": "Lunch", + "description": null, + "startsAt": "2024-11-01T11:55:00", + "endsAt": "2024-11-01T12:55:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 }, { - "id": 39641, - "name": "Booth", + "id": 54706, + "name": "Ask Android", "session": { - "id": "530186", - "title": "Coding for Good: Achieving social change with an app", - "description": "As coders, we have an enviable ability to create platforms and apps that bring people together. The world is not short of challenges right now, especially climate change, but we will only have a real impact on the world if we can help people unite and organise to pressure those with power and influence.\r\n\r\nChris and his husband quickly developed an app ten years ago to allow ordinary people to lobby the normally inaccessible and unelected House of Lords to pass the Same Sex Marriage Bill. It unexpectedly took off, with 15,000 emails sent in the space of two weeks, helping the Bill overwhelmingly succeed in the normally-socially-conservative upper House of Parliament in the UK.\r\n\r\nHe'll talk here about how he did it, and how you - an individual with a computer and knowledge of how to knock an app together - can use this skill to give a voice to the voiceless, and to potentially change or even save the world.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "764391", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:55:00", + "endsAt": "2024-11-01T12:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f53882b1-df66-4d6c-9ec1-888badcdd224", - "name": "Chris Ward" + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 - }, + "index": 6 + } + ] + }, + { + "slotStart": "12:55:00", + "rooms": [ { - "id": 39642, - "name": "Lamarr", + "id": 49270, + "name": "Hangouts", "session": { - "id": "518103", - "title": "Nested Navigation in flutter web", - "description": "In Flutter web, developing Nested navigation is always a critical topic. \r\nEvery project has different needs, Some project needs Bottom navigation some need Sidebar navigation, and some need some sidebar with nested navigation. Too many combinations and complications!! it’s a hard decision to decide on an approach similar to the packages. Which package should we use and why? In this talk, I am sharing my story about Go Router. How Shell route is a change maker and solved many problems. Let’s learn more about the Shell route. and its different use cases. Make Flutter web navigation simple and easy.\r\n", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "700387", + "title": "Mistakes you make using Kotlin Coroutines", + "description": "Kotlin Coroutines offer a powerful, yet deceptively simple solution for managing asynchronous tasks. However, their ease of use can sometimes lead us into unforeseen pitfalls. From unexpected cancellations to perpetually running operations, developers often encounter bewildering behaviors that can affect application performance and reliability. In this presentation, we'll dive deep into the common mistakes made while using Kotlin Coroutines. We'll explore why these issues arise, how to diagnose them, and, most importantly, how to resolve them effectively. Expect to walk away with advanced insights and practical strategies to harness the full potential of coroutines in your Kotlin applications, ensuring they're robust, efficient, and maintainable.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "990dea5b-f32e-4260-8551-967e2597bee2", - "name": "Renuka Kelkar" + "id": "ef7c1115-d852-44b9-9f72-530ba7223c55", + "name": "Marcin Moskala" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185727, - "name": "Flutter" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "11:50:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "98fa317f-118e-4210-86a0-6d6516600458", - "title": "Break", - "description": null, - "startsAt": "2023-10-27T11:50:00", - "endsAt": "2023-10-27T12:05:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 - } - ] - }, - { - "slotStart": "12:05:00", - "rooms": [ + }, { - "id": 36716, - "name": "Lovelace", + "id": 49271, + "name": "Glass", "session": { - "id": "513549", - "title": "Blast Off: Managing Hundreds of UI Updates for Emoji Cannons", - "description": "Managing a state might be a challenge. Managing the state with hundreds of updates and constant recomposition of floating emojis is a challenge indeed. In this talk, I will share how to build emoji cannon that floods your screen with UI elements, update the state, and does not freeze your UI. All of that with Jetpack Compose :) \r\nIn addition to covering the technical aspects of building and managing a state, I will also share how to build a responsive UI that takes the user input and animates.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "736513", + "title": "A Snapshot Preview of Paparazzi 2.0", + "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this session, we'll discuss:\r\n* why pixel perfection is challenging af\r\n* image encoding and why we chose APNG for animations\r\n* why Google's publishing of layoutlib is great for the community\r\n* ...and more!\r\n\r\nWe'll also give a sneak peek to what's coming in 2.0, as well as why we're excited about it and you should be too!", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b6dc4b56-ca5d-4e55-8a57-e37f6e6d6af0", - "name": "Piotr Prus" + "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", + "name": "John Rodriguez" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 1 }, { - "id": 36717, - "name": "Hamilton", + "id": 49272, + "name": "Things", "session": { - "id": "502207", - "title": "Take your shot of Vitamin!", - "description": "Decathlon has more than 160 frontend products, including 50 dedicated to mobile applications. Due to this context, it is hard to align the user interface across all these projects while respecting the platform.\r\n\r\nVitamin is a Design System developed by Decathlon as a product which can be adapted to any context and with multiple technical implementations for Android, iOS and Web. In theory, you can use this Design System in your application and customize it to fit your theme and your needs.\r\n\r\nIn this presentation, I'll focus on Vitamin Compose, the design and technical architecture, biases and what are the next steps.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "731650", + "title": "Elevated Dependency Injection: Going Beyond the Basics with Custom Hilt Components", + "description": "Hilt/Dagger is a popular and powerful building block in Modern Android Development. Its benefits include reduced boilerplate and compile-time magic to ensure runtime safety and performance. \r\n\r\nBy default, Hilt provides basic components, such as Singleton, Activity, or ViewModel. \r\nIt is also extensible, allowing you to create additional components for more complex use cases. \r\n\r\nHere at Patreon, building a custom UserComponent tied to a user session has improved our codebase immensely by reducing boilerplate, increasing code safety, and making it easier to cancel work when a user logs out.\r\n\r\nThis talk will focus on the ins and outs of custom components and some simple but non-obvious techniques that we’ve used to make them nicely integrate into our app!\r\n\r\nHighlights of the talk will include:\r\n\r\n- Custom components and when you should use them\r\n- The beautiful synergy of dagger components and coroutine scopes\r\n- Overcoming the limitations of Dagger’s single inheritance and Hilt’s fixed component hierarchy\r\n- Providing easy access to bindings in `@HiltViewModel` and `@HiltAndroidTest` classes\r\n- A case study of Patreon’s `UserComponent`", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", - "name": "Gerard Paligot" + "id": "1caff1f9-9594-4ce8-a031-ed1ba2201d13", + "name": "Steven Kideckel" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185736, - "name": "Design" + "id": 264384, + "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264387, + "name": "Dagger" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 2 }, { - "id": 36718, - "name": "Liskov", + "id": 53981, + "name": "Stadia", "session": { - "id": "495165", - "title": "How to handle incidents at scale", - "description": "In this session, I want to guide the audience into applying healthy processes to handle incidents. We have all probably been in the situation of having our apps unexpectedly crashing - either because of a bad release, because of unexpected Backend changes, etc.\r\n\r\nIn these scenarios, we usually get overwhelmed by messages from different departments (support folks, managers, and so on), this is especially true when operating at a large scale, and things can quickly get out of control.\r\n\r\nThis talk aims to propose rules that will not only mitigate the above scenarios but also improve the communication flow, give tips about how to set up alerting systems and what to measure, and talk about a \"post-mortem\" process.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "700492", + "title": "Ten things you heard about testing that might be wrong", + "description": "Testing became an essential part of Android development. Many conference talks have been given and even more best practices have been written. \r\n\r\nBut what if, as time evolved, some of the things we thought were true, changed?\r\n\r\nLet’s start questioning some of these in this talk:\r\n- Are flaky tests fixable?\r\n- Are mocks even harmful?\r\n- Is DI about testing?\r\n- Did we understand testing in isolation properly? \r\n- Is the test pyramid still valid?\r\n- Is Robolectric good or bad on Android?\r\n- And in times of AI, should we generate tests?\r\n\r\nCome and join my session to learn more!\r\n", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ac9cef8a-3072-4e52-8064-a5155be542b0", - "name": "Alessandro Mautone" + "id": "4d3a31b1-61e5-4a26-9ca3-c9f30e3eaa08", + "name": "Danny Preussler" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 3 }, { - "id": 39640, - "name": "Hopper", + "id": 53982, + "name": "Nest", "session": { - "id": "528097", - "title": "Beyond the Basics: Performance Monitoring and User Experience for Mobile App Growth", - "description": "When interacting with a mobile app that regularly crashes or freezes, 53% of users uninstalled the app, 37% stopped using it, and 28% looked for a replacement. Users are no longer forgiving mobile app mishaps and errors. Going beyond the basic metrics of understanding your user experience and diving deep into mobile performance is key to ensuring growth and positive user experiences. We will highlight which metrics and takeaways mobile teams should track to create a superior app experience that takes into account every user interaction. Find out why app insights, proactive issue detection, advanced debugging, and alert management capabilities should matter to you and the development of your mobile app.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "735538", + "title": "Unblocking The Main Thread: Solving ANRs and Frozen Frames", + "description": "In the realm of Android development, the main thread is our stage, but too often, it becomes a battleground where performance issues arise, leading to ANRs, frozen frames, and sluggish Uls. As we strive for excellence in user experience, understanding and optimizing the main thread becomes essential to prevent these common performance bottlenecks.\r\n\r\nWe have strategies and best practices for keeping the main thread uncluttered. We'll examine the root causes of performance issues and techniques for monitoring and improving main thread health as well as app performance.\r\n\r\nIn this talk, participants will walk away with practical knowledge on enhancing app performance by mastering the main thread. We'll share proven approaches to eliminate real-life ANRs and frozen frames to build apps that deliver butter smooth experience.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6519e3d1-ca80-46b3-99f5-0f77adfdc91f", - "name": "Sean Higgins" + "id": "9a7f5add-ce7a-49a3-ab3d-f3c4484453e8", + "name": "Sinan Kozak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185727, - "name": "Flutter" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185731, + "id": 264380, "name": "Tooling" }, { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264379, + "name": "Android Studio" }, { - "id": 185742, + "id": 264391, "name": "Firebase" - }, - { - "id": 185744, - "name": "sponsor" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 4 + }, + { + "id": 54703, + "name": "Chromecast", + "session": { + "id": "74a094cd-2f41-4906-8427-7c720694b385", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 }, { - "id": 39641, - "name": "Booth", + "id": 54706, + "name": "Ask Android", "session": { - "id": "511354", - "title": "Refactoring and Test Fakes: Crafting Resilient Code with Confidence", - "description": "Crafting resilient code is one of the most important things we do as software developers, but it's much easier said than done! Building with confidence requires an appropriate test harness and automated safeguards to ensure your software is robust.\r\n\r\nIn most real world scenarios, we don't have the luxury of working with a green field project, so it can be difficult to apply best practices whilst maintaining legacy code. How then can we refactor, and effectively utilise test fakes appropriately?\r\n\r\nIn this talk, I'll discuss the best approaches for using test fakes, mocks, stubs, and what are the pros and cons for each. How we can avoid writing brittle tests, slowing down development, and build scalable apps that can stand the test of time.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "764396", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", - "name": "Ash Davies" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 4 + "index": 6 + } + ] + }, + { + "slotStart": "13:35:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "7370ab67-e2bb-4288-8de1-d5012f5278b2", + "title": "Break", + "description": null, + "startsAt": "2024-11-01T13:35:00", + "endsAt": "2024-11-01T13:50:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 }, { - "id": 39642, - "name": "Lamarr", + "id": 54706, + "name": "Ask Android", "session": { - "id": "521242", - "title": "How not to ship an app. Lessons and experiences shipping bugs to production.", - "description": "Have you ever experienced the anxiety of shipping a mobile app to production, with your fingers crossed that nothing is going to go wrong? If so then this is the talk for you, and if it is not, maybe you will learn something new.\r\n\r\nOver the past 2+ years, we have shipped a lot of bugs in our production app, with this talk I aim to condense the most important learnings we had and the actions we took to mitigate those. Openly sharing our experiences will not only help you avoid similar pitfalls but also foster a culture of learning and improvement within the Flutter community.\r\n\r\nWhether you are an aspiring developer, an experienced professional, or a team lead, this talk will offer valuable insights and practical tips to improve your app's quality.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "764397", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:35:00", + "endsAt": "2024-11-01T13:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "758dff56-ba98-4d4d-ab60-87472b2b4846", - "name": "Efthymios Sarmpanis" + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - "index": 5 - } - ] - }, - { - "slotStart": "12:25:00", - "rooms": [ - { - "id": 36716, - "name": "Lovelace", - "session": { - "id": "a0f5131a-50a1-4336-a76f-9db2d9e89524", - "title": "Lunch", - "description": null, - "startsAt": "2023-10-27T12:25:00", - "endsAt": "2023-10-27T13:25:00", - "isServiceSession": true, - "isPlenumSession": true, - "speakers": [], - "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": null + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 + "index": 6 } ] }, { - "slotStart": "13:25:00", + "slotStart": "13:50:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "538652", - "title": "Why is my app letterboxed?!", - "description": "Have you ever wondered why your app is running in a small window in the middle of the screen, with bars on the sides? This usually happens on larger screens, but why?\r\n\r\nThis is called letterboxing, which is an Android app compatibility mode the platform places an app in if it cannot support a certain window size. In this talk, we will explore why letterboxing happens and how to optimize your app to avoid it. We will also discuss how to ensure that your app looks good on all screens, regardless of the screen orientation or aspect ratio.\r\n\r\nBy the end of this talk, you will be able to:\r\n- Identify the causes of apps running in letterbox mode.\r\n- Understand how to fix this issue.\r\n- Ensure that your app looks good on all devices.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "733770", + "title": "User Initiated Data Transfer Jobs", + "description": "Google introduced a new api UIDT in Android 14 as an alternative to Work Manager and Foreground Services.\r\n\r\nA deep dive session into how your apps can run longer-duration, user-initiated transferring of data, such as downloading a file from a remote server using UIDT. With increasing restrictions on running Foreground Services it is very important to migrate your app's critical use cases to alternate API's. Android 14 applies strict rules on when you can run a Foreground Service & Android 15 is bringing 6 hours timeout to Foreground Services.\r\n\r\nWill share how specific use cases migrated to UIDT can lead to improved app performance.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7d912e80-f02c-42cf-92db-98e670bef1a6", - "name": "Roberto Orgiu" + "id": "edd46ee9-3e55-478c-8f27-c8f86ca77138", + "name": "Tushar Varshney" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" - }, - { - "id": 185739, - "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "495137", - "title": "Practical ADB usage to enhance your life!", - "description": "ADB is an incredibly underutilized tool that can dramatically improve your Android development experience! Maybe you know some basic ADB commands, perhaps even a couple of nifty advanced ones or possibly, you don’t know what ADB even is!\r\n\r\nImagine automatically & swiftly logging into your app’s test account, instantly inspecting your app’s current Activity/Fragment stack or editing your app’s shared preferences & phone settings all without touching your device! All this and SO MUCH MORE is possible thanks to ADB.\r\n\r\nThis session is not intended to blast you with a list of ADB commands but instead to share a collection of practical scenarios & examples how you can use this supreme tool everyday in a variety of ACTUALLY useful ways. \r\n\r\nJoin me in this session, regardless of your experience, as we explore what this tool can really do to change the way you work forever!", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734509", + "title": "Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components", + "description": "Are your ViewModels exponentially growing out of control as they manage the state for each of your Composables? This talk introduces Molecule, a new library for creating state holders in Jetpack Compose. We’ll explore how Molecule can simplify presentation state management in Compose, allowing for more flexible and scalable UI development. Attendees will learn how to implement Molecule in their projects and understand its advantages over traditional state management approaches as well as some of the drawbacks to this approach.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", - "name": "Benjamin Kadel" + "id": "6624cb6c-7fc4-4495-a87f-8060e88d7d18", + "name": "Jack Adams" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185731, - "name": "Tooling" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264362, + "name": "Flow" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 1 }, { - "id": 36718, - "name": "Liskov", + "id": 49272, + "name": "Things", "session": { - "id": "495999", - "title": "A Picture Is Worth A 1,000 ... Lines? Devs?: Get the full picture (on image loading) with Coil", - "description": "To download an image on Android, all you need are these lines of code:\r\n val input = URL(“https://cutecatsimage”).openStream()\r\n val bitmap = BitmapFactory.decodeStream(input)\r\n imageView.setImageBitmap(bitmap)\r\nWhy then, are there whole projects and teams dedicated to doing this on Android like Coil, Picasso, Glide, etc. In this talk we’ll focus on Coil and look under the hood to break down the inner workings of Coil’s image downloading pipeline. We’ll go way beyond using `imageViewload(“https://cutecatsimage”)`, and dive deep into Coil’s source code to understand how it handles loading your image into memory, caching, and transforming your image. You’ll walk away from this talk with a rich understanding of the journey the images you’re working with take to show up on your screen, and a being in a much better spot to work with problems that could arise.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "736447", + "title": "You don't have to run it locally! How to run your emulators in the cloud.", + "description": "Probably every Android engineer has tried running Android Emulator on their laptops. Many companies have experience running Android Emulators in CI pipelines for testing. But what does it take to run a highly interactive emulator in the cloud? Why would one need to run an emulator in the cloud?\r\n\r\nAt Uber we know how to build Cloud Development Environments. And now we expanded into Android Emulator space. Providing our engineers with zero setup emulators. \r\n\r\nWe'll walk you through all the moving parts of the emulator setup in a dev environment that sparks joy. Share the challenges we had and opportunities we discovered.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "a13934a7-6137-4a94-9a03-84919fce12f4", - "name": "Rafa Moreno" + "id": "695e56a6-62a8-496b-9082-8cc4dadec337", + "name": "Petras Vičiūnas" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" + "id": 264380, + "name": "Tooling" }, { - "id": 185739, - "name": "Libraries" + "id": 264394, + "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 2 }, { - "id": 39640, - "name": "Hopper", + "id": 53981, + "name": "Stadia", "session": { - "id": "529953", - "title": "Why can't my app open that file? A deep dive into the Android app sandbox", - "description": "Android keeps a close eye on what files your application can read and write. We call this the \"application sandbox\": a safe area for your code to run which prevents other apps from interfering with your data, and prevents you from interfering with other apps or the operating system\r\n\r\nIn this talk I will be looking at the rationale behind the sandbox, and how it works. I will look at file ownership, file access modes and the reasons for those \"Permission denied\" messages. I will dig down to find out exactly why an app can access only a well defined set of private files and shared storage. And I will explain\r\nhow SE Linux enforces all of this at at a deep level\r\n\r\nYou will be reassured that Android is a secure operating system. But, as you know there are always exceptions to the rules. So, in the final section of this talk I will show you how preinstalled system apps can crash though all the barriers\r\n", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734664", + "title": "Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.", + "description": "With the advent of Android 15, edge-to-edge design has become the default configuration. Consequently, applications must be capable of accommodating window insets, including the system status bar and navigation bar, as well as supporting drawing under display cutouts and other system UI elements. This presentation will address the most prevalent challenges encountered when supporting such a diverse range of UI configurations. Additionally, it will illustrate how to utilize Android previews and testing with Jetpack Compose to effectively navigate this complexity and guarantee an optimal user experience across all scenarios.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5d3a7bdc-4d73-4985-91f2-a388978732a9", - "name": "Chris Simmonds" + "id": "257fcb0c-cc84-4892-a1f1-54985071e0a6", + "name": "Timo Drick" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185724, - "name": "API" + "id": 264363, + "name": "Testing" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185736, - "name": "Design" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "496982", - "title": "Improving Video Playback with ExoPlayer", - "description": "Video has become an integral part of our life, and we are witnessing a significant rise in the integration of video content within Android apps. Sadly, there isn't much information out there about videos on Android. I've personally had a hard time finding practical ways to make videos better.\r\n\r\nIn this talk, my primary focus will be on sharing practical approaches with ExoPlayer that go beyond what is documented: We'll discuss the common problems with playbacks, solutions and will find a performant approach to use ExoPlayer together with Jetpack Compose.\r\n\r\nI'll share everything that I learned practically during the last 8 months:\r\n- Video basics: Media3, ExoPlayer and how it works\r\n- Bandwidth and it's role\r\n- Adaptive streamable protocols (HLS/Dash) vs custom ABR implementation\r\n- Caching and Video Prefetching\r\n- Initial video resolution improvements\r\n- Time to the first frame improvements\r\n- Performance with Jetpack Compose\r\n- Architectural Approaches\r\n\r\nEverything that will be mentioned is validated through real production scenarios and confirmed an efficiency by A/B tests. ", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "704275", + "title": "The state of code coverage for Kotlin", + "description": "Nowadays it’s not anymore a question whether we want to write tests or not. Writing unit tests is not “nice-to-have” it’s an essential part of our everyday job. While that’s already a great step forward, how can we be sure our tests are actually testing something. To check this we all use code coverage tools.\r\n\r\nIn this presentation you will learn how code coverage for Kotlin works, which tools we can use and what are the challenges and limitations of various approaches. You will learn the difference between line, instruction and condition coverage and how to read and interpret coverage results in Kotlin.\r\n\r\nIn the last part of this presentation we will cover the future for Kotlin Code Coverage, what is missing and what can be improved in the future.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "61cc0eba-ac00-414b-9cf7-5525040a4bdd", - "name": "Alexey Bykov" + "id": "eeb25beb-5d37-4fb7-acac-170357a1d89b", + "name": "Marharyta Nedzelska" + }, + { + "id": "d2ddc9e3-35b6-44a9-8daa-144d89cabaff", + "name": "Evgeny Mandrikov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, - { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ { - "id": 185735, - "name": "Android" + "id": 264359, + "name": "Kotlin" }, { - "id": 185739, - "name": "Libraries" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", + "session": { + "id": "00e42524-0d24-448a-b3e6-8dc57352c3d7", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", "session": { - "id": "530144", - "title": "Deep Dive into Flutter Animations: Crafting Dynamic and Engaging UIs", - "description": "Animations are more than just visual flair; they breathe life into mobile applications, making them feel responsive and intuitive. In this session, we'll embark on a deep dive into Flutter's animation toolkit. From the foundational principles of animating widgets to advanced techniques like gesture-driven transitions and physics-based movements, attendees will discover how to elevate their app's user experience to new heights. Through hands-on demonstrations, learn to harness the full potential of Flutter's animation capabilities and craft truly dynamic and engaging UIs.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "764398", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6284f70b-bae3-4b49-a672-52b5b7ca5124", - "name": "Josteve Adekanbi" + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "14:05:00", + "slotStart": "14:30:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "ced93252-06e3-4f68-8af3-afd0bbc137b7", + "id": "2cf43443-53aa-439d-b2e3-b3cb3f96c326", "title": "Break", "description": null, - "startsAt": "2023-10-27T14:05:00", - "endsAt": "2023-10-27T14:20:00", + "startsAt": "2024-11-01T14:30:00", + "endsAt": "2024-11-01T14:50:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764399", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:30:00", + "endsAt": "2024-11-01T14:50:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 } ] }, { - "slotStart": "14:20:00", + "slotStart": "14:50:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "520809", - "title": "TextField in Jetpack Compose: past, present and future", - "description": "Text input is a fundamental text component used in practically all apps at some point, from simple usage in a login screen to complex dynamic server-driven forms.\r\nIn this talk we’ll recap current text input APIs and their shortcomings, in particular for state management. As well we’ll explore the brand new BasicTextField2 API surface for common scenarios along with filtering, selection, gestures and more. The API you've been waiting for is coming to you soon :)", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "693397", + "title": "Crime Scene InvestiGITor", + "description": "THERE HAS BEEN A MURDER!\r\n...\r\n(Or whatever the code version of a murder is... like breaking unit tests, a bug maybe?... I dunno!)\r\n\r\nTogether, we will learn how to become a professional and revered investiGITor, who will be able to sniff out and solve any version control offence with the ease and panache of a seasoned detective!\r\n\r\nVersion control software is often a mysterious black-box that we HAVE TO interact with in order to successfully collaborate with others. But what if it doesn't need to be a confusing & complicated enigma?\r\n\r\nYou will learn to probe into the dark recesses of Git and understand its inner workings by learning how to carry out many tasks that you will undoubtedly need to perform at some point in your career.\r\n\r\nFor example:\r\n* Ever needed to safely remove a secret that you accidentally stored in the repository?\r\n* Ever had your app break, not know why, and then needed to quickly hunt down the exact commit where a bug was introduced?\r\n* Ever needed to travel back in time, through history, to stop a crime before it even happens...(sort of)?\r\n\r\n...All these things and a bunch more useful & interesting, lesser-known ways to become a masterful git detective and truly understand the most important tool in a developer's arsenal.\r\n\r\nSo come have a little fun with me in this talk, bring the bugs to justice, solve the case of the naughty commit & become a hero by defending the integrity of your codebase!", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "18c45d50-3e38-4933-a55b-8a833f850115", - "name": "Alejandra Stamato" - }, - { - "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", - "name": "Anastasia Soboleva" + "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", + "name": "Benjamin Kadel" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185708, - "name": "Kotlin" + "id": 264369, + "name": "Other" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264380, + "name": "Tooling" }, { - "id": 185735, - "name": "Android" + "id": 264394, + "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "518266", - "title": "Making Data Visualizations More Accessible - Lessons Learned", - "description": "Graphs are a great way to visualize different types of data. But not everyone can consume them the same way - and if you add, e.g., touch interactions, not everyone can use the pointer the same way. Also, not everyone understands graphs the same way - due to prior familiarity, disability, or other reasons. And what if you can't see at all? That is another factor that adds complexity to data visualizations.\r\n\r\nIn this talk, I will share some tips and demonstrate how you can improve the accessibility of your graphs so that they work for different types of users - whether they are using assistive technologies or not. You'll get actionable advice to take to your apps and improve their accessibility immediately. You will learn about adding text alternatives for data visualization, adding alternatives for touch, and assuring that color is not the only way to communicate information.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "720685", + "title": "Why is adaptive layout a nightmare?", + "description": "Dive into the captivating realm of Android UI development in this session where we will share in-depth expertise on designing versatile user interfaces. Together, we will explore the complex challenges of organizing and building UI components, providing unique insights into crafting seamless user experiences across a variety of devices such as phones, foldables, and tablets.\r\n\r\nWhile Google advocates for the use of adaptive layout, we will unveil the often underestimated nuances, shed practical light on overcoming obstacles you’ll meet down the road and help you create truly adaptive Android applications.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", - "name": "Eeva-Jonna Panula" + "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", + "name": "Gerard Paligot" + }, + { + "id": "81f9ecbf-c08c-48c7-850f-1e79e1439867", + "name": "David Ta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185714, - "name": "Accessibility" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264367, + "name": "Foldables" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 1 }, { - "id": 36718, - "name": "Liskov", + "id": 49272, + "name": "Things", "session": { - "id": "495565", - "title": "Our ongoing journey from REST to GraphQL on Android", - "description": "Here is the story of an ongoing migration... and what a migration! This journey moving from our REST API to the GraphQL one is a long run with much coordination across all tech teams.\r\n\r\nThis return of experience will focus on how we are dealing with this transition on Android where Retrofit coexists with Apollo, how we synchronized with backend teams to keep our GraphQL schemas up-to-date or how we are dealing with our authentication stack with Apollo Kotlin, or the issues we faced and much more.\r\n\r\nBuckle up! Relax! The journey is just starting 🚜", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "734218", + "title": "Why WhatsApp uses ListView", + "description": "Using outdated ListView on one of the most used screens screens sounds outrageous. Will talk about technical and organisational reasons why and how WhatsApp still uses ListView and what are the challenges preventing migration. ", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "462e6b97-01f9-44b1-864c-9d85bedf911b", - "name": "Julien Salvi" + "id": "e11a53fd-1cfb-4930-9dfa-42187e94d869", + "name": "Hadi Tok" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185724, - "name": "API" + "id": 264360, + "name": "Soft Skills" }, { - "id": 185735, + "id": 264384, "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 2 }, { - "id": 39640, - "name": "Hopper", + "id": 53981, + "name": "Stadia", "session": { - "id": "529650", - "title": "Honest mistakes caused by shallow interpretation of SOLID", - "description": "Although coding, in essence, is a clear subject that normally doesn’t open space for too many points of view with clear rights and wrongs, it’s naive to think that the same problem can’t be correctly solved by different architectures or even opposed solutions.\r\n\r\nBy taking SOLID concepts as an argument to support their cases, less experience engineers tend to not take context and product requirements into account, risking the failure of the initiative for the sake of following good practices by the book.\r\n\r\nMany times it’s also common that just couple principles are taken into account, completely ignoring others that might even be more important given the scope of the problem.\r\n\r\nThis talk is about recognizing these destructive patterns on your team when designing a solution to help you to Built it Right, as long as you are Building the Right Thing.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "741888", + "title": "Monetizing Your Side Project to $1k in Monthly Revenue and Beyond", + "description": "Only ~17% of apps reach $1k in monthly revenue. Learn best practices on crossing $1k MRR and beyond from personal experience building two profitable apps and advice from other successful app founders.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b44c9f22-4d83-4246-bf67-bdd30af9bfef", - "name": "Haroldo Olivieri" + "id": "0e4f3abe-cc6c-409a-8f34-e346f23c0cc4", + "name": "Jeffrey Bunn" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, + "id": 264360, + "name": "Soft Skills" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264369, "name": "Other" + }, + { + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "522982", - "title": "Staying Relevant", - "description": "When working on legacy projects and slow to change codebases, it can feel like you are being left behind, while others are actively learning and using the latest technologies.\r\n\r\nI want to share my approach with you, to help keep on top of things and remove any FOMO.\r\n\r\nThe inspiration for this talk comes from working on a legacy project for the last couple of years.\r\nI’ve felt like I was becoming worse at my job, that I’m becoming outdated and falling behind. I want to help people not feel the way I felt.\r\nBeing at a conference seeing all the latest and greatest things can reinforce those feelings, so I will help bring everything back into perspective.\r\n\r\nSpecifically, I will outline a series of strategies you can follow, from day to day activities to thinking in the years of your career, and a specific set of recommendations for our current state in 2023.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "719034", + "title": "Navigation in a Multiplatform World: Choosing the Right Framework for your App", + "description": "Navigation in mobile, desktop, and web applications is such a fundamental part of how we structure our architecture. In order to both obtain functional clarity, and abstraction from platform level implementation.\r\n\r\nFor a long time, there have been options available specific to each platform, and even options part of the platform framework itself. Though it can be difficult to find the right option for platform-agnostic code, ensuring consistency. Some go one step further, providing an opinionated guide on how to architecture your application.\r\n\r\nIn this talk, I'll evaluate the options available, how they differ, and to what type of applications they are best suited. Including how to get started with them, and the best practice guidelines on how to get the most out of them, for your application.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", - "name": "Neil Hutchison" + "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", + "name": "Ash Davies" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" + "id": 264359, + "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", + "session": { + "id": "f431dc83-3136-4892-8c38-b289aac3710f", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", "session": { - "id": "528773", - "title": "Rolling in the deep(link) - take a deep dive into Flutter navigation", - "description": "Deep linking is an essential feature in mobile apps that allows users to access specific pages or sections of an app from an external source like a link or notification. \r\n\r\nIn this talk, I will cover everything you need to know about handling deep links in Flutter mobile apps. \r\n\r\nWe'll start by exploring an independent solution for handling deep links in Flutter, followed by discussing third-party solutions that offer additional features such as dynamic link generation and deferred deep links. We'll also examine the benefits and limitations of using these solutions and how to implement them in a Flutter app. \r\n\r\nThroughout the talk, we'll discuss common issues and solutions when handling deep links in Flutter apps. \r\n\r\nAttendees will leave with a comprehensive understanding of deep linking in Flutter and the knowledge to implement deep linking solutions in their mobile apps.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "764400", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "d63cf645-9a0e-434b-bebe-4a3825755cb5", - "name": "Alicja Ogonowska" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "15:00:00", + "slotStart": "15:30:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "413af535-507d-4b62-954f-ebfd58548f09", + "id": "c1cc8017-64f6-44c1-9c63-ac4116e75ea0", "title": "Break", "description": null, - "startsAt": "2023-10-27T15:00:00", - "endsAt": "2023-10-27T15:15:00", + "startsAt": "2024-11-01T15:30:00", + "endsAt": "2024-11-01T15:45:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 + }, + { + "id": 54706, + "name": "Ask Android", + "session": { + "id": "764404", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:30:00", + "endsAt": "2024-11-01T15:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 6 } ] }, { - "slotStart": "15:15:00", + "slotStart": "15:45:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "496334", - "title": "90s Website … in 2023 on mobile in Compose … for science", - "description": "Why would anyone build something with a 90s website aesthetic - in Compose? Nostalgia? For one the 90s website aesthetic made heavy use of animations and visual effects. So emulating this style will take any animation framework through its paces. This talk will take some of the most iconic 90s website elements and demonstrate how to reproduce them to build a retro mobile experience using Jetpack Compose. \r\n\r\nI can’t promise the end result will have a good user experience but I can promise a good look at the animation system in Compose and a collection of cheesy code snippets.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "717692", + "title": "Level up your SDKs with KMP - no rewrite required!", + "description": "There are several compelling reasons to explore adding Kotlin Multiplatform (KMP) to your existing SDKs or libraries. The main one - to reduce development time and cut maintenance costs by having a single codebase for shared logic.\r\n\r\nBut what if you already have a suite of tried and tested native SDKs, and want to simply add KMP capabilities to broaden your audience, without impacting existing customers? Or perhaps you’re adding functionality on top of your SDKs and want to build it once instead of duplicating it in 2, 3, or 4 programming languages?\r\n\r\nI’ll share our experiences at PubNub with bringing three of our realtime messaging SDKs - TypeScript, Swift and JVM, under a unified KMP API. I’ll also show how this foundation then enabled us to build and ship a new Chat SDK on top, built with KMP from the ground up and targeting all 3 platforms.\r\n\r\nThe talk will cover the initial requirements phase, the architectural decisions, and the tradeoffs that we had to make to finally land on KMP as the chosen solution.\r\n\r\nWe'll then delve into practical code examples and solutions (as well as quite a few WTF moments!) that allowed us to build the Chat SDK without affecting our existing users, and even improved the quality and consistency of our native SDKs.\r\n\r\n", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "2313d8eb-7d6e-444d-8e8d-4d223f896ce8", - "name": "Maia Grotepass" + "id": "ffd6d0b9-9615-4f5a-9f58-6dcf3d4acb3b", + "name": "Wojtek Kaliciński" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264374, + "name": "API" }, { - "id": 185732, + "id": 264381, "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 0 }, { - "id": 36717, - "name": "Hamilton", + "id": 49271, + "name": "Glass", "session": { - "id": "527245", - "title": "Multiplatform USB: How to Communicate Seamlessly", - "description": "At the droidcon Berlin we as the GDG Berlin Android introduced 'ZeBadge' a digital name badge, programmed by a companion Android app.\r\n\r\nSadly it was limited to only Android companions. But not anymore! Let me introduce ZeKompanion: A multiplatform app that communicates with ZeBadge, no matter the platform (except for iOS).\r\n\r\nThis talk will guide through the architecture of the app, describing the abstractions taken and explain how you can leverage Kotlin Multiplatform for Mobile to abstract the platform specific USB communication from the Compose UI displayed on all devices.\r\n\r\nFollow here for an interesting journey about USB Serial communication, Kotlin and KMM, Jetpack Compose and how to boil it all together into one app: ZeKompanion.\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "730918", + "title": "Upgrading Meta's Kotlin Infrastructure to K2: A Migration Journey", + "description": "In this talk, we will share our experience migrating Meta's Kotlin infrastructure from K1 to K2. We will discuss the challenges we faced during the migration process, including differences in error handling between K1 and K2, and how we overcame them. We will also cover the process of migrating compiler plugins, including overview of plugins we migrated and the solutions we implemented to overcome challenges. Finally, we will discuss our experience with the KSP2.0 migration.\r\nThis talk will provide valuable insights for developers and teams considering a migration to K2, as well as those interested in learning more about the challenges and solutions involved in large-scale migrations.\r\n\r\nKeywords: K2, Kotlin 2.0, Kotlin compiler plugins", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "fc80cf34-563e-4cd4-a04d-23d1191a7812", - "name": "Mario Bodemann" + "id": "33801296-8d41-4df0-b729-c0461afa1be2", + "name": "Ruslan Latypov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185710, - "name": "KMP" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185720, - "name": "IoT" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" + "id": 264374, + "name": "API" }, { - "id": 185730, - "name": "Android Studio" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 1 }, { - "id": 36718, - "name": "Liskov", + "id": 49272, + "name": "Things", "session": { - "id": "528020", - "title": "Trust no one - Introducing the Play Integrity API", - "description": "You put a lot of time and effort into developing your application, but you're then releasing it into the wild without any security measures in place. Your app could be modified and requests to your backend server could be coming from unknown and unsafe environments, which makes your services vulnerable to attack and abuse. The Play Integrity API helps your app's backend server to take appropriate actions to prevent attacks and reduce abuse by detecting potentially risky and fraudulent interactions.\r\nIn this talk we will cover the API with a focus on:\r\n- How to setup and use the API\r\n- Low-latency use cases with the new standard request\r\n- Migrating from the SafetyNet Attestation API\r\n- How to test and manage errors\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "731808", + "title": "Streamlining Permission Request in Jetpack Compose", + "description": "Managing Android permission requests often involve considerable boilerplate code, and while Jetpack Compose offers many advantages, it has not simplified this aspect. It can add complexity if not properly designed. This talk will demonstrate an efficient method for creating a reusable Android permission request flow with minimal boilerplate using Jetpack Compose. By the end of this session, you will learn how to implement a reusable permission request handler for single and multiple permissions. Additionally, you'll discover how to provide custom permission rationale messages and design a custom permission request UI with minimal boilerplates, enhancing developer experience and application usability.\r\n\r\nKey Takeaways\r\n1. Understanding Permission Management in Android: A quick overview of the challenges in handling permission requests within the context of Android development.\r\n\r\n2. Identifying Boilerplate in Permission Requests: Examination of the typical boilerplate code associated with permission requests in Jetpack Compose.\r\n\r\n3. Efficient Permission Handling: Step-by-step guide on creating a reusable permission request handler for single and multiple permissions.\r\n\r\n4. Customization Techniques: How to implement custom permission rationale messages and design custom UI components for permission requests.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e5941ea5-5a10-45a0-903b-f642db802a16", - "name": "Pietro Maggi" + "id": "3b95e79a-0e6b-4fcd-b44e-023fa3dfd7cd", + "name": "Mayowa Egbewunmi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" + "id": 264358, + "name": "Jetpack Compose" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 2 }, { - "id": 39640, - "name": "Hopper", + "id": 53981, + "name": "Stadia", "session": { - "id": "529128", - "title": "Navigation superpowers at your fingertips", - "description": "This talk will begin by the demonstration of a beautiful sample app built with Compose Mulitplatform and Appyx, complete with:\r\n\r\n- Shared element transitions\r\n- Scoped dependencies\r\n- Deep links that animate the app into a desired state\r\n- Gesture-controlled navigation\r\n\r\nWhile this is a wide range of topics, we’ll see how they belong together in one cohesive story, where they’re just different aspects of the same underlying theme: managing application state.\r\n\r\nIn this highly visual talk we’ll also see how using Appyx we can add the UI magic of custom transitions and gesture-control to our apps in no time.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "735413", + "title": "Let's build a performance measuring tool from scratch", + "description": "Did you know you can measure the performance of production apps? Android is based on Linux, so that gives us a lot of power!\r\nThrough live-coding, let's have fun exploring system files, low-level events and the Android source code to see how we can gather metrics (FPS, CPU, RAM) on your favourite apps!\r\nYou'll gain more insights on how Android work under the hood and why measuring performance can be crucial but complex.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94bb39bf-8256-4633-adcd-54d621242cea", - "name": "Zsolt Kocsi" + "id": "ae4dc258-34a6-462c-b778-807da7d25de4", + "name": "Alexandre Moureaux" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185710, - "name": "KMP" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185741, - "name": "UI/UX" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 3 }, { - "id": 39641, - "name": "Booth", + "id": 53982, + "name": "Nest", "session": { - "id": "530295", - "title": "Now smile (also in 3D)! Exploring AR, ML and Camera related APIs on Android", - "description": "When it comes to Camera APIs, Android has come a long way since its start, which is good, not only for the platform consistency above Lollipop but also because now we can increase its potential by using AR and ML APIs integrations, such as AR Core, ML Kit and MediaPipe. \r\n\r\nHowever, it can bit confusing if you're not familiar with the existing available APIs, which one to use for specific scenarios and how to approach Camera2, and CameraX in general.\r\n\r\nDuring this talk, you will learn the basics of camera APIs on Android and how to extend them to different use cases to enhance your app use experience!", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "709201", + "title": "Passwordless Future !!", + "description": "Passwords: no one likes them.\r\n\r\nWith so many requirements—capital letters, numbers, symbols—and the challenge of remembering a strong one, passwords often become more of a hassle than a security measure. Even the most complex passwords can be vulnerable to hackers who can guess, steal, or trick you into revealing them.\r\n\r\nThankfully, a better solution exists, which we will explore in today’s session: Passkeys.\r\n\r\nWhat You'll Learn:\r\n- An introduction to Passkeys and the primary reasons for their implementation.\r\n- Integration of Passkeys and demos into Android apps using the credential API.\r\n- How users can sign in to apps and websites using biometric sensors (fingerprint or facial recognition), PINs, or patterns, creating a seamless sign-in experience that eliminates the need for usernames or passwords.\r\n- Best practices for implementing Passkeys in your applications to maximize both security and user convenience.\r\n- Case studies and examples of successful Passkey integration in real-world applications.\r\n\r\nTarget Audience:\r\nThis talk is ideal for mobile developers of all levels who are interested in:\r\n- Smoothing the login experience for their users across various platforms.\r\n- Enhancing security for their apps.\r\n- Staying ahead with modern authentication methods to improve user retention and satisfaction.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "226909f8-7c0a-43bc-b296-a3f2c369b9a3", - "name": "Walmyr Carvalho" + "id": "0037b354-6bd4-46e3-b1e6-d84916eea0d6", + "name": "Ayushi Gupta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264384, + "name": "Android" + }, + { + "id": 264385, + "name": "Design" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", + "session": { + "id": "e4ce5c39-ca56-4b02-854f-ad4c89ea88da", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", "session": { - "id": "527829", - "title": "Seamless Interoperability: Harnessing JNIGen and FFIGen", - "description": "This talk dives into the world of seamless interoperability, unveiling the power of JNIGen and FFIGen in enabling direct access to native methods from Dart code. Unlike the current approach of using Platform channels in Flutter, JNIGen, and FFIGen allows us to achieve effortless access to native methods from Dart code. By eliminating the need for manual method handling and channel management, these tools revolutionize the development process. With practical examples and performance comparison, attendees will discover how JNIGen simplifies integration with Android libraries and JAVA classes, while FFIGen streamlines access to Swift and Objective-C libraries. ", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "764406", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76ce51b1-7dd4-4858-98de-0b51b3c9abcb", - "name": "Akanksha Singh" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" }, { - "id": "d9cf6863-3524-4461-b5d2-ee91cc1595f2", - "name": "Kendi J" + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 } ] }, { - "slotStart": "15:55:00", + "slotStart": "16:25:00", "rooms": [ { - "id": 36716, - "name": "Lovelace", + "id": 49270, + "name": "Hangouts", "session": { - "id": "b90c49a2-817f-4b03-9339-f93257d0e7e5", + "id": "059a58e3-50b1-4a62-8377-7263b7ca960f", "title": "Break", "description": null, - "startsAt": "2023-10-27T15:55:00", - "endsAt": "2023-10-27T16:10:00", + "startsAt": "2024-11-01T16:25:00", + "endsAt": "2024-11-01T16:40:00", "isServiceSession": true, "isPlenumSession": true, "speakers": [], "categories": [], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": null + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 0 - } - ] - }, - { - "slotStart": "16:10:00", - "rooms": [ + }, { - "id": 36716, - "name": "Lovelace", + "id": 54706, + "name": "Ask Android", "session": { - "id": "538661", - "title": "A guide to using foreground services and background work in Android 14", - "description": "Coroutines, WorkManager, Foreground Services, Oh my! \r\nHave you ever felt confused on how to ensure your application is kept alive to continue work after it's left the foreground?\r\n\r\nThere have been many changes over the past few Android releases regarding the background work and Foreground Services. In this talk we will break down: \r\n- What is background work vs foreground service vs asynchronous work?\r\n- What’s new with Foreground Services in Android 14?\r\n- We'll provide a framework to help you decide what API to use in Android 14\r\n- We'll go over some common misconceptions around WorkManager or Foreground Services\r\n\r\nAs a bonus we’ll also discuss what the future of managing background work and Foreground Services will look like beyond Android 14.", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764408", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:25:00", + "endsAt": "2024-11-01T16:40:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "05c58f24-23c5-4bce-a4f5-9b610f7084d3", - "name": "Alice Yuan" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 0 - }, + "index": 6 + } + ] + }, + { + "slotStart": "16:40:00", + "rooms": [ { - "id": 36717, - "name": "Hamilton", + "id": 49270, + "name": "Hangouts", "session": { - "id": "530286", - "title": "From Laptop Builds to Advanced CI", - "description": "How do you transition from the solo-coder mindset to building a robust, automated CI pipeline that supercharges your team?\r\n\r\nOnce you get a basic pipeline running there are numerous aspects to evaluate:\r\n\r\n* Are the results useful?\r\n* You start to add unit test results and collect build artifacts. Is it fast and reliable enough that the team benefits from it?\r\n* Do you need a merge queue or do you need better checks?\r\n* What is consistently missed that you can automate?\r\n* What tedious repetitive tasks can you transform into a welcome resource?\r\n* Are you hitting resource limits that require an engineering investment to mitigate?\r\n* Do you invest in Gradle or Bazel? What JVM options are right for your codebase?\r\n* Is Docker useful? How far should you go in your optimizations?\r\n* How do you secure the pipeline?\r\n \r\nWe'll delve into each topic and share how to apply our learnings to empower you. Along the way we will discuss how to approach stakeholders outside engineering to demonstrate the value it brings to a business. Join us for a saga of struggles and victories and how we transformed our CI pipeline at a modern scale-up business.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "732470", + "title": "Exploring Kotlin Symbol Processing: A Practical Guide", + "description": "In this session we’ll dive into the world of Kotlin Symbol Processing (KSP). This session aims to provide an introduction to KSP and its benefits compared to the Kotlin Annotation Processing Tool (KAPT).\r\n\r\nThe practical portion of this talk will guide you through the process of creating annotation definitions and implementing a symbol processor. We will demonstrate the usage of KSP API and KotlinPoet for generating Kotlin files, providing you with a hands-on experience of working with KSP. Furthermore, we will demonstrate how to use KSP in multiplatform projects.\r\n\r\nBy the end of this talk, you will walk away with a solid understanding of Kotlin Symbol Processing, and practical knowledge on how to leverage KSP in your development workflow.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "96e519b2-b0e1-4232-a71c-21bf3145cb13", - "name": "Jason Pearson" + "id": "f589d95b-8262-4337-b6c8-78b4701afa3a", + "name": "Dean Djermanović" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185730, - "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 1 + "index": 0 }, { - "id": 36718, - "name": "Liskov", + "id": 49271, + "name": "Glass", "session": { - "id": "510034", - "title": "Hot Take: Engineering Managers are actually useful!", - "description": "Is your manager pulling their weight, or just a waste of space? Should they exist just to protect the people doing the *actual work* from ignorant execs? \r\n\r\nTruthfully, managing any human is a science and an art, and managing engineers certainly isn't easier! A good manager is a leader, architect, consiglieri, and career coach… and yes, sometimes the person who moves JIRA tickets around a kanban board!\r\n\r\nIn this talk, we'll address some common hot takes about engineering management and talk about the transition from engineer to manager. Whether you want to become a manager or continue as an individual contributor, you'll leave with a better sense of what your manager actually does!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "735915", + "title": "Android driver - application of Android device in RC vehicle development", + "description": "TL;DR: This talk will showcase development of remote controlled vehicle with Android powered device as its central unit. \r\n\r\n... and now long version:\r\nStudents and researches around the world often struggle in early stages of development of robotics and hardware projects. The reason? High cost and connection difficulties of all the sensors and effectors for Arduinos, Raspberry Pi's or whatever platform they do choose. All of them have a solution right in their hands - or their pockets - and that is with almost no additional costs. \r\n\r\nBy enabling Android device to act as a part, or center, of embedded system you can unlock vast variety or its capabilities - ones that would cost hundreds of dollars to purchase and dozens of hours to connect & set up. Some of them are:\r\n- LTE access to Internet\r\n- WiFi & bluetooth connectivity\r\n- screen, speakers, microphone, LED light\r\n- accelerometer, gyroscope\r\n- multiple cameras\r\n- biometric sensors\r\nNot to mention higher level of services they offer through countless libraries ready to be used by developers.\r\n\r\n-> By the end of this talk, attendees will discover an innovative & easy way to use Android powered devices to cut down costs of prototyping hardware projects, especially as proposed, by building RC vehicle. ", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7a816772-cc23-42e7-818e-4fd5153f7283", - "name": "Parth Padgaonkar" + "id": "58038e33-703e-497c-9f47-3624e36418e8", + "name": "Tomasz Słuszniak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, + "id": 264366, + "name": "Modularization" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264369, "name": "Other" + }, + { + "id": 264370, + "name": "IoT" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 2 + "index": 1 }, { - "id": 39640, - "name": "Hopper", + "id": 49272, + "name": "Things", "session": { - "id": "530595", - "title": "Sink or swim: proving ideas in production, fast", - "description": "It all began with a beer and a video call: a group of friends had an app idea and wanted to see whether it could work out. But going from idea to shipping something on the Play Store isn’t that easy. How do you organize work? What are the roles and tools to use? And most importantly, how to build a framework onto which you can iterate on multiple ideas, fast?\r\n\r\nThat’s what we had to figure out over the past six months as we launched multiple ideas into production. Join us in a retrospective of how we got from that famous beer to today. We’ll cover how an Android dev such as yourself can organize work to prioritize fast iteration, pick the right tools and technologies, automate the heck out of everything, figure out how to promote and grow the app, plan a business strategy, and understand if it is sustainable.\r\n\r\nBy attending this session, you’ll find out what worked out for us and what didn’t in this process, and how to avoid some mistakes we made. If you’ve ever had the itch to publish an app — by yourself or with some friends — come along to find out what’s ahead of you!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "734441", + "title": "A journey in Android's BLE world", + "description": "Working with BLE on Android could be overwhelming, there are a lot of OSS libraries to pick from, different permissions to be declared, and sometimes different behaviors depending on the Android version and device.\r\n\r\nThis talk aims to make order between what's available out there: we'll start from the basics of how BLE works, climbing the ladder and going more high-level examining tools and libraries using Kotlin features to simplify observing data and freeing up unused resources, including an exploration of latest JetPack library AndroidX Bluetooth.\r\n\r\nYou'll find real-life scenarios, examples, and strange issues you may encounter (along with even stranger fixes).\r\n\r\nWhether you plan to integrate a BLE device in your app, improve the existing code to be more expressive and use modern libraries, or even only scan for Bluetooth beacons, follow me on this journey in the BLE world.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "83d33e86-8cca-44a8-b0dc-2994332ef8e8", - "name": "Ivan Morgillo" - }, - { - "id": "8fc4211d-38ef-4661-acee-16b4ea9cc800", - "name": "Aurelio Laudiero" - }, - { - "id": "f897009d-854d-4403-ab47-44c29d8edce1", - "name": "Daniele Bonaldo" + "id": "8253d551-1437-4a3e-b4f4-56bf73373109", + "name": "Paolo Rotolo" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185724, + "id": 264374, "name": "API" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" }, { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185733, - "name": "AI/ML" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185742, - "name": "Firebase" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 3 + "index": 2 }, { - "id": 39641, - "name": "Booth", + "id": 53981, + "name": "Stadia", "session": { - "id": "529420", - "title": "Unlocking Unity for Android Developers: Blending Android and Unity in Harmony", - "description": "We've all become accustomed to working on Android projects as part of our daily work. But have you ever thought about how you could seamlessly adapt your existing solutions to play nice with other engines like Unity? Or perhaps you’re considering creating an entirely new library to incorporate functionalities that aren't readily available in the core Unity. \r\n\r\nSimilarly, Unity paves the way for hybrid Android development, allowing you to embed Unity-created elements, mini-games and augmented reality directly into your Android apps.\r\n\r\nIn this talk, we're delving into the Unity landscape from an Android developer's perspective. We'll unravel the intricate interplay between Unity scripts and Android code, gaining insights into how IL2CPP and Mono scripting backends operate to seamlessly unite C# and Kotlin.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "706060", + "title": "Overcoming Unsecurities in WebViews", + "description": "Is your relationship with WebViews healthy? Sometimes you can't avoid the need to display web content in your app. It can be a functionality that you need to release quickly and it's already implemented by web devs in your team. It can be just a Terms and Conditions page you need to show. Often the reason for putting these into WebViews is that the latest version must be displayed without requiring an app update.\r\n\r\nSo web content tends to make its way into many apps. It's not obvious that by adding a single WebView, you can open up your app for abuse by malicious actors. Google made steady progress in making WebViews more secure by default but often you can't stop supporting those old, vulnerable OS versions. Ultimately it's your responsibility to secure your WebViews and the default settings are not always right. This talk aims to help with that while also highlighting security issues that lurk in those seemingly simple yet quite complex APIs.\r\n\r\nYou would learn the importance of always sanitizing inputs and restricting capabilities to what is actually needed. If you want to take one piece of advice from the talk, you should use more modern APIs like Custom Tabs, JavaScript Engine, or AndroidX's WebView variant.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bb2df6d6-edb0-416a-85fc-83b062c6c10c", - "name": "Yevhen Pekutovskyi" + "id": "6b808c1f-836d-4349-9585-a89355dfb8d0", + "name": "Balázs Gerlei" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185719, - "name": "Other" - }, - { - "id": 185735, - "name": "Android" + "id": 264372, + "name": "Security" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + "index": 3 + }, + { + "id": 53982, + "name": "Nest", + "session": { + "id": "2823786d-8670-4816-801e-9579b16c87cc", + "title": "Coming Soon!", + "description": null, + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": null, + "isInformed": false, + "isConfirmed": false }, "index": 4 }, { - "id": 39642, - "name": "Lamarr", + "id": 54703, + "name": "Chromecast", + "session": { + "id": "4e3e8d41-d83c-4f91-8742-bf75d4b54436", + "title": "T3 Engineering Leadership Summit (Separate Ticket Required)", + "description": "https://www.t3-summit.com/t3-london", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", + "isServiceSession": true, + "isPlenumSession": false, + "speakers": [], + "categories": [], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 5 + }, + { + "id": 54706, + "name": "Ask Android", "session": { - "id": "530074", - "title": "Bring the power of Postgres to your Flutter app with Supabase", - "description": "Supabase is an open-source Firebase alternative centered around Postgres database, one of the world’s most popular databases known for its extensibility, performance, and data integrity. You can create a fresh Supabase project with just a few clicks, and you will gain access to a fully managed Postgres database with serverless APIs, real-time database subscription, auth, file storage, and edge functions. With its Flutter SDK, developers can access the Supabase database securely and efficiently, helping them build complex apps in a matter of days. \r\n\r\nIn this session, I will give you an overview of what Supabase is, and how it is helping developers build scalable applications efficiently. We will also look at a few examples where advanced data types of Postgres, such as geo data, or range data, can help you build complex Flutter applications efficiently. \r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764409", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "31f0ce3c-630f-4146-a6f5-9e6c7f5cad62", - "name": "Tyler Shukert" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185724, - "name": "API" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, - "index": 5 + "index": 6 + } + ] + }, + { + "slotStart": "17:20:00", + "rooms": [ + { + "id": 49270, + "name": "Hangouts", + "session": { + "id": "c9009c93-af8c-4487-ae2f-253f17acea6a", + "title": "See you next year!", + "description": null, + "startsAt": "2024-11-01T17:20:00", + "endsAt": "2024-11-01T17:30:00", + "isServiceSession": true, + "isPlenumSession": true, + "speakers": [], + "categories": [], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": null, + "isInformed": false, + "isConfirmed": false + }, + "index": 0 } ] } diff --git a/shared/src/commonMain/resources/speakers.json b/shared/src/commonMain/resources/speakers.json index cf9c45c83..5c9cb1348 100644 --- a/shared/src/commonMain/resources/speakers.json +++ b/shared/src/commonMain/resources/speakers.json @@ -6,17 +6,21 @@ "fullName": "Adam Ahmed", "bio": "Senior Android, mediocre cyclist, amateur home cook, and coffee nerd. He/Him 🌱", "tagLine": "Senior Android Engineer", - "profilePicture": "https://sessionize.com/image/4a78-400o400o2-LcDqwm1AJ7ChjgJ1yQYFiC.jpg", + "profilePicture": "https://sessionize.com/image/b0eb-400o400o1-LcDqwm1AJ7ChjgJ1yQYFiC.jpg", "sessions": [ { - "id": 501854, - "name": "Developer Productivity On a Budget" + "id": 757537, + "name": "Roundtable: Mastering Release Management" + }, + { + "id": 725157, + "name": "Click-free releases & the making of a CLI app" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/oheyadam", "linkType": "Twitter" }, @@ -40,35 +44,25 @@ "categories": [] }, { - "id": "76ce51b1-7dd4-4858-98de-0b51b3c9abcb", - "firstName": "Akanksha", - "lastName": "Singh", - "fullName": "Akanksha Singh", - "bio": "Akanksha is a Software Engineer at JP Morgan Chase & Co., crafting engaging and innovative software solutions in the Asset and Wealth Management Line of Business. She has a keen interest in Flutter and enjoys building user-friendly and accessible applications. Her passion for Flutter has taken her to international conferences like FlutterHeroes, FlutterCon, and Flutter & Friends where she has shared her insights and expertise.\r\n\r\nBeyond her role, Akanksha is an avid participant in hackathons. Her passion for community building and mentorship has driven her to lead student communities at university and mentoring roles at prestigious hackathons like HackHarvard, Hack4Inclusion, HackTheNorth, DubHacks, CalHacks, and more. \r\n\r\nWhen she steps away from her keyboard, Akanksha finds solace in the timeless melodies of 60s-70s Hindi songs. She's an avid museum-goer, ignited by a deep passion for delving into art and history.", - "tagLine": "Software Engineer at JP Morgan Chase & Co.", - "profilePicture": "https://sessionize.com/image/28cf-400o400o2-sZ8qevh2buTGRUqZDEDr7v.jpeg", + "id": "532dc16d-b69d-4f68-aa49-fe9878b0b52f", + "firstName": "Adarsh", + "lastName": "Fernando", + "fullName": "Adarsh Fernando", + "bio": "Adarsh is a Senior Product Manager on Android Studio, focusing on the code editor, debugger, device access, Google service integrations, and testing.", + "tagLine": "Google", + "profilePicture": "https://sessionize.com/image/3dde-400o400o1-e7bdf209-26ed-4e56-bbc9-8fcd0b9b5eea.jpg", "sessions": [ { - "id": 527829, - "name": "Seamless Interoperability: Harnessing JNIGen and FFIGen" + "id": 747072, + "name": "Scalable Testing Strategies" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/coder_jedi", - "linkType": "Twitter" - }, - { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/akanksha1212/", - "linkType": "LinkedIn" - }, - { - "title": "Instagram", - "url": "https://www.instagram.com/akankshasingh1212/", - "linkType": "Instagram" + "title": "Company Website", + "url": "http://d.android.com", + "linkType": "Company_Website" } ], "questionAnswers": [], @@ -79,19 +73,19 @@ "firstName": "Alejandra", "lastName": "Stamato", "fullName": "Alejandra Stamato", - "bio": "Alejandra is an Android software engineer and TL at HubSpot. Formerly Android Developer Relations Engineer at Google for Jetpack Compose (2021-2023).\r\nShe's worked for multiple apps in the past, gathering a tenure of ~10 years in the industry.", - "tagLine": "Lead Android engineer at HubSpot", - "profilePicture": "https://sessionize.com/image/3645-400o400o2-J6h16BQL9QPP8ftbpFxgrp.png", + "bio": "Alejandra is an Android software engineer and TL at HubSpot. Formerly Android Developer Relations Engineer at Google for Jetpack Compose (2021-2023). She's worked for multiple apps in the past, gathering a tenure of ~10 years in the industry.", + "tagLine": "Tech Lead, HubSpot", + "profilePicture": "https://sessionize.com/image/df22-400o400o1-J6h16BQL9QPP8ftbpFxgrp.png", "sessions": [ { - "id": 520809, - "name": "TextField in Jetpack Compose: past, present and future" + "id": 733921, + "name": "Effective App Monitoring in Production" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/astamatok", "linkType": "Twitter" }, @@ -99,120 +93,111 @@ "title": "LinkedIn", "url": "https://www.linkedin.com/in/alejandra-stamato/", "linkType": "LinkedIn" + }, + { + "title": "Blog", + "url": "https://www.linkedin.com/in/alejandra-stamato/", + "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://www.hubspot.com/", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "ac9cef8a-3072-4e52-8064-a5155be542b0", - "firstName": "Alessandro", - "lastName": "Mautone", - "fullName": "Alessandro Mautone", - "bio": "I started coding around the age of 18 and immediately fell in love with it. Since I was always passionate about mobile (first with Nokia and theirs Symbian and then with Android), it was natural for me to continue on that path. I started working in a small agency in South Italy where I had the opportunity to work on a lot of projects, then decided to expand my horizons and moved to Amsterdam where I started to work at WeTransfer. After almost 5 years there, I decided to pursue an industry I have at heart: bikes! And started working for Canyon.\r\n\r\nParticular signs: I love sports in general, I am a regular runner and since love everything that flies I am a proud paraglider!", - "tagLine": "Canyon - Lead Android Engineer", - "profilePicture": "https://sessionize.com/image/9790-400o400o2-sdZRTXLcbBKo5emNmNq9kc.jpg", + "id": "81ee4554-4839-4192-9a74-ecb32dff2819", + "firstName": "Alex", + "lastName": "Vanyo", + "fullName": "Alex Vanyo", + "bio": "I'm an Android developer with a love of details. As a Developer Relations Engineer, I'm trying to improve APIs, write samples and share knowledge so that everyone can create polished, testable and beautiful apps. My currently focuses are on Jetpack Compose for large screens and Wear.", + "tagLine": "Android Developer Relations Engineer @ Google", + "profilePicture": "https://sessionize.com/image/b85b-400o400o1-n3DMwB2jY2nA2Kyrorew13.jpg", "sessions": [ { - "id": 495165, - "name": "How to handle incidents at scale" + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/Alexs784", + "url": "https://twitter.com/alex_vanyo", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/alexs784", + "url": "https://www.linkedin.com/in/alexvanyo/", "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://www.linkedin.com/in/alexs784/", - "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://www.canyon.com", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "61cc0eba-ac00-414b-9cf7-5525040a4bdd", - "firstName": "Alexey", - "lastName": "Bykov", - "fullName": "Alexey Bykov", - "bio": "I’m Alexey, Senior Android Software Engineer & Android GDE based in London.\r\nCurrently I work at Reddit where where I'm improving video experience on the main android app. \r\n\r\nFor most of my career, I have been working with large products with millions of active users. I got into\r\nsoftware development thanks to people who are actively sharing knowledge through blog posts and free\r\ncourses. This is why I spend a lot of my spare time sharing content with the community, and I sincerely enjoy it. \r\nOver these years, I have given dozens of talks and written over 20 articles about Android.\r\n\r\nPassionate about Kotlin, functional programming, Jetpack Compose, UI testing and Gradle.", - "tagLine": "Senior Software Engineer at Reddit & Android GDE", - "profilePicture": "https://sessionize.com/image/2954-400o400o2-HbXDx8XjAcS4PtAzQ6oy1M.png", + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "firstName": "Alex", + "lastName": "Vanyo", + "fullName": "Alex Vanyo", + "bio": null, + "tagLine": "Developer Relations Engineer on Android", + "profilePicture": "https://sessionize.com/image/aba8-400o400o1-NMeqfRJDBGc7y2i6XgMPX2.jpg", "sessions": [ { - "id": 496982, - "name": "Improving Video Playback with ExoPlayer" - } - ], - "isTopSpeaker": false, - "links": [ + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" + }, { - "title": "Twitter", - "url": "https://twitter.com/nonewsss", - "linkType": "Twitter" + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" }, { - "title": "LinkedIn", - "url": "https://uk.linkedin.com/in/alexeybykov", - "linkType": "LinkedIn" + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" }, { - "title": "Blog", - "url": "https://medium.com/@nonewss", - "linkType": "Blog" + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "05c58f24-23c5-4bce-a4f5-9b610f7084d3", - "firstName": "Alice", - "lastName": "Yuan", - "fullName": "Alice Yuan", - "bio": "Alice is a developer relations engineer at Google work with developers on background execution and battery performance.\r\nPrior to Google, she's been building for Android for the past 6 years at various startups including Pinterest and Patreon.\r\nShe has a passion for teaching, and volunteers for various non profit organizations teaching youths to code!", - "tagLine": "Developer Relations Engineer at Google", - "profilePicture": "https://sessionize.com/image/fe79-400o400o2-4xMTwz1oFwzWjaaU1K6HMm.jpg", + "id": "ae4dc258-34a6-462c-b778-807da7d25de4", + "firstName": "Alexandre", + "lastName": "Moureaux", + "fullName": "Alexandre Moureaux", + "bio": "Alex has been developing mobile apps for the past 8 years, previously with React Native and more recently with Compose & Flutter as well. He’s obsessed with performance and created Flashlight 🔦, a lighthouse for mobile apps. He truly believes it's our responsibility to develop performant apps, as one way to fight against climate change!", + "tagLine": "Performance Expert at Theodo Apps", + "profilePicture": "https://sessionize.com/image/38d9-400o400o1-58-34a6-462c-b778-807da7d25de4.c4417a84-7a42-487e-846b-9fdb6542db5d.jpeg", "sessions": [ { - "id": 538661, - "name": "A guide to using foreground services and background work in Android 14" + "id": 735413, + "name": "Let's build a performance measuring tool from scratch" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/Alice__Yuan", + "title": "X (Twitter)", + "url": "https://twitter.com/almouro", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/aliceyuan", + "url": "https://www.linkedin.com/in/alexandremoureaux", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://aliceintechland.org", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://google.com", + "url": "https://apps.theodo.tech/", "linkType": "Company_Website" } ], @@ -220,34 +205,34 @@ "categories": [] }, { - "id": "d63cf645-9a0e-434b-bebe-4a3825755cb5", - "firstName": "Alicja", - "lastName": "Ogonowska", - "fullName": "Alicja Ogonowska", - "bio": "I am a mobile app developer with 7 years of experience. I started as an Android developer and then moved to Flutter over three years ago, never regretting the change! Currently I work at EQUIQO where I'm developing a mobile app for flaconi.\r\nIf I don't code, I probably cook or eat.", - "tagLine": "Flutter Developer at EQUIQO", - "profilePicture": "https://sessionize.com/image/df54-400o400o2-wFexMLi5bbBzxqKzGXgQQ3.jpg", + "id": "af079576-c7db-497a-990d-919fc2472e45", + "firstName": "Anastasia", + "lastName": "López", + "fullName": "Anastasia López", + "bio": "From crafting Android apps to leading engineering teams, Anastasia blends her passion for Android development with her expertise as a seasoned Engineering Manager.", + "tagLine": "Engineering Manager", + "profilePicture": "https://sessionize.com/image/b31b-400o400o1-76-c7db-497a-990d-919fc2472e45.8933491f-ac5c-4745-acec-e85b5aacc2d9.jpg", "sessions": [ { - "id": 528773, - "name": "Rolling in the deep(link) - take a deep dive into Flutter navigation" + "id": 757518, + "name": "Roundtable: EM or IC? What’s Right for Me?" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/AlicjaOgonowska", + "url": "https://twitter.com/AnastasiaLopezD", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/alicja-ogonowska/", + "url": "https://www.linkedin.com/in/anastasia-lopez-418b6225/", "linkType": "LinkedIn" }, { "title": "Company Website", - "url": "https://equiqo.com", + "url": "https://www.expedia.co.uk/", "linkType": "Company_Website" } ], @@ -259,13 +244,13 @@ "firstName": "Anastasia", "lastName": "Soboleva", "fullName": "Anastasia Soboleva", - "bio": "Anastasia has worked on Jetpack Compose for a few years now. Focusing on layout, accessibility and material text fields, she more recently moved to the Compose Text team.", + "bio": "Anastasia has worked on Jetpack Compose for a few years now focusing on text and accessibility.", "tagLine": "Software engineer at Google", - "profilePicture": "https://sessionize.com/image/97a2-400o400o2-U97pjJkSmUf8MR7D6rFMNT.jpg", + "profilePicture": "https://sessionize.com/image/cd66-400o400o1-4mADg2nqsz4ryysCJFFdFj.jpg", "sessions": [ { - "id": 520809, - "name": "TextField in Jetpack Compose: past, present and future" + "id": 736303, + "name": "Text in Compose: Beyond the Basics" } ], "isTopSpeaker": false, @@ -274,74 +259,70 @@ "categories": [] }, { - "id": "2e8bbf8c-544f-460d-8145-78f6693c8ee4", - "firstName": "André", - "lastName": "Oriani", - "fullName": "André Oriani", - "bio": "A Brazilian living in Silicon Valley for the past 9 years, André works professionally with Android since its first public beta release. He worked on the development of the Instant Messaging app and on the Homescreen of the first Motorola Android devices, including the famous Droid series. Currently, he is a tech lead and a Principal Software Engineer at Walmart.", - "tagLine": "Principal Software Engineer @ Walmart", - "profilePicture": "https://sessionize.com/image/0176-400o400o2-JwQxEDnZVkt2T25E4vRqL5.jpg", + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "firstName": "Anton", + "lastName": "Beloglazov", + "fullName": "Anton Beloglazov", + "bio": "Anton has recently joined the Jetpack Compose team at Google, previously working on the Google App for Android for several years.", + "tagLine": "Software Engineer – Google", + "profilePicture": "https://sessionize.com/image/93b0-400o400o1-3z6EVrcWxXZcSYrxSD4p97.jpg", "sessions": [ { - "id": 522881, - "name": "Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love" - } - ], - "isTopSpeaker": false, - "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/aoriani", - "linkType": "Twitter" + "id": 764343, + "name": "Ask Android! - Compose, GenAI, WearOS" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/aoriani/", - "linkType": "LinkedIn" + "id": 764345, + "name": "Ask Android! - Compose, GenAI, Platform" }, { - "title": "Blog", - "url": "https://medium.com/@aoriani", - "linkType": "Blog" + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" }, { - "title": "Company Website", - "url": "https://www.walmart.com", - "linkType": "Company_Website" + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "c6100c4b-13c4-433e-8dcd-ea0c92939179", - "firstName": "Andreas", - "lastName": "Luca", - "fullName": "Andreas Luca", - "bio": "I enjoy fancy apps, as well as everything related to cybersecurity and innovation in technology. However, I ensure you that I did not write this biography with Chat-GPT.\r\nTogether with the founders I started Build38 and, now, I have the privilege of working in the verge of technology and business. I help customers in different industries such as banking, eID, car manufacturers, start-ups, protect their solutions.", - "tagLine": "Head of Solution Integration @ Build38", - "profilePicture": "https://sessionize.com/image/e96a-400o400o2-sp9PAWPdSqky6YWWg77C1s.jpg", + "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", + "firstName": "Ash", + "lastName": "Davies", + "fullName": "Ash Davies", + "bio": "Google Developer Expert for Android and Kotlin, enthusiastic public speaker, senior engineer, Kotlin aficionado, Multiplatform manipulator, prolific facilitator of cute cat photographs, spends more time travelling than working (he/him)", + "tagLine": "Senior Android Developer", + "profilePicture": "https://sessionize.com/image/29f9-400o400o1-Vcm3WTHo4f6iT85wrGnmfc.jpg", "sessions": [ { - "id": 540429, - "name": "REST in Peace: A Journey Through API Protection" + "id": 719034, + "name": "Navigation in a Multiplatform World: Choosing the Right Framework for your App" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/theBIGGlucas", + "title": "X (Twitter)", + "url": "https://twitter.com/askashdavies", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/andreas-luca/", + "url": "https://www.linkedin.com/in/ash-davies-8290b3268/", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://ashdavies.dev/", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://build38.com/", + "url": "https://ashdavies.dev/", "linkType": "Company_Website" } ], @@ -349,64 +330,39 @@ "categories": [] }, { - "id": "1e19e15d-18c9-4cd9-82b2-280120a8c8a6", - "firstName": "Andrei", - "lastName": "Shikov", - "fullName": "Andrei Shikov", - "bio": "Some people call me an engineer from time to time. Kotlin full-timer since before traits became interfaces.", - "tagLine": "Compose @ Google", - "profilePicture": "https://sessionize.com/image/6b98-400o400o2-5d-18c9-4cd9-82b2-280120a8c8a6.6f94432f-c80a-4dfc-b910-0e9e51864072.jpg", - "sessions": [ - { - "id": 529751, - "name": "Performance in Compose: Lessons learned from Lazy layouts" - } - ], - "isTopSpeaker": false, - "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/shikasd_", - "linkType": "Twitter" - } - ], - "questionAnswers": [], - "categories": [] - }, - { - "id": "f3facba0-c34d-4c87-a7e7-a81a67c8029d", - "firstName": "Antoine", - "lastName": "Danois", - "fullName": "Antoine Danois", - "bio": "Antoine is an Android Developer at TAG Heuer based in Paris. Before that, he worked at Citymapper in London and Canal+ in Paris. When he's not working to help people catch their buses in big cities, he's watching videos of rockets and space related things, also his desk is named \"Legoland\" by his colleagues. Part of the Droidcon London and Android Makers Program Committee, and organiser for the Paris Android User Group, he's doing his best to have amazing conferences in London, because the community is really important in the world of Android. And otherwise, he's just eating cheese.", - "tagLine": "Senior Android Developer at TAG Heuer, Paris", - "profilePicture": "https://sessionize.com/image/a9d5-400o400o2-4GVqRUuS3H3VRQhU5FqJ4f.jpeg", + "id": "f40ca183-824d-43d9-9e63-ee0edb5aa307", + "firstName": "Ataul", + "lastName": "Munim", + "fullName": "Ataul Munim", + "bio": "Ataul is an Android developer based near London, UK. He joined the Google Developers Experts programme in 2016 with a specialisation in accessibility and inclusive design on Android, and is currently working at Kraken Technologies with Octopus Energy as a mobile lead and manager.", + "tagLine": "Mobile Lead/EM at Octopus Energy", + "profilePicture": "https://sessionize.com/image/f596-400o400o1-95ArqBvH3isArv8Xwnjvw7.jpg", "sessions": [ { - "id": 528710, - "name": "Go the extra mile for your media app on Android" + "id": 757518, + "name": "Roundtable: EM or IC? What’s Right for Me?" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/Antoinedroid", + "url": "https://twitter.com/ataulm", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/antoinedanois/", + "url": "https://www.linkedin.com/in/ataulm/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "http://antoine.dk", + "url": "https://ataulm.com", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://citymapper.com/", + "url": "https://monzo.com/", "linkType": "Company_Website" } ], @@ -414,39 +370,34 @@ "categories": [] }, { - "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", - "firstName": "Ash", - "lastName": "Davies", - "fullName": "Ash Davies", - "bio": "Google Developer Expert for Android and Kotlin, enthusiastic public speaker, senior engineer at Snapp Mobile, Kotlin aficionado, Multiplatform manipulator, prolific facilitator of cute cat photographs, spends more time travelling than working (he/him)", - "tagLine": "Senior Android Developer @ Snapp Mobile GmbH", - "profilePicture": "https://sessionize.com/image/7162-400o400o2-3sSDTUEzJHmdfEFfg8AvLq.jpg", + "id": "8e2c2869-4beb-4868-8651-8567219ea947", + "firstName": "Aurimas", + "lastName": "Liutikas", + "fullName": "Aurimas Liutikas", + "bio": "Aurimas is a software engineer at Google working on AndroidX libraries. The need to write efficient code has been instilled in him through his work on Chrome for Android, and later Android OS itself. This drive continued when taking on the build and test infrastructure for AndroidX - one of the largest set of open source libraries built using Gradle. Aurimas enjoys doing talks, blog posts, and generally pushing the Gradle ecosystem forward.", + "tagLine": "Software Engineer at Google / Gradle Fellow", + "profilePicture": "https://sessionize.com/image/60da-400o400o1-MFJXsV41DKtQAUwir77pyQ.jpg", "sessions": [ { - "id": 511354, - "name": "Refactoring and Test Fakes: Crafting Resilient Code with Confidence" + "id": 711892, + "name": "Gradle: The Build System That Loves To Hate You" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/askashdavies", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/ash-davies-8290b3268/", + "url": "https://www.linkedin.com/in/liutikas", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://ashdavies.dev/", + "url": "https://www.liutikas.net/blog-posts", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://snappmobile.io/", + "url": "https://www.google.com", "linkType": "Company_Website" } ], @@ -454,64 +405,49 @@ "categories": [] }, { - "id": "8fc4211d-38ef-4661-acee-16b4ea9cc800", - "firstName": "Aurelio", - "lastName": "Laudiero", - "fullName": "Aurelio Laudiero", - "bio": "Passionate about development, training, reading and walking surrounded by nature.", - "tagLine": "Freelancer", - "profilePicture": "https://sessionize.com/image/97f5-400o400o2-667a2c1b-58f7-46b4-91c6-41f4b4b8ee17.jpg", + "id": "0037b354-6bd4-46e3-b1e6-d84916eea0d6", + "firstName": "Ayushi", + "lastName": "Gupta", + "fullName": "Ayushi Gupta", + "bio": "With my coding skills, I specialize in crafting diverse application solutions for Android mobile and TV platforms. My goal is to innovate and overcome any obstacles in our fast-paced, ever-evolving world.\r\nDriven by a passion for problem-solving, I eagerly accept challenges and strive to deliver impactful solutions. Let's connect and collaborate to find innovative solutions together", + "tagLine": "GDE Firebase, WTM Ambassador,Senior Android Engineer @Zalando, ", + "profilePicture": "https://sessionize.com/image/961b-400o400o1-wob3DCgQ1sgwMkZDHMnoNG.jpeg", "sessions": [ { - "id": 530595, - "name": "Sink or swim: proving ideas in production, fast" + "id": 709201, + "name": "Passwordless Future !!" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/home", + "title": "X (Twitter)", + "url": "https://twitter.com/DroidyAyu", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/aureliolaudiero/", + "url": "https://www.linkedin.com/in/ayushi-gupta-a37368125/", "linkType": "LinkedIn" - } - ], - "questionAnswers": [], - "categories": [] - }, - { - "id": "e99fa2a9-4e01-4534-907d-00dbb1447360", - "firstName": "Ben", - "lastName": "Thompson", - "fullName": "Ben Thompson", - "bio": "Ben is the Developer Relations Lead at Licel. He works with developers and security professionals at organisations worldwide to protect apps from reverse engineering, tampering, and data theft.", - "tagLine": "Developer Relations Lead, Licel", - "profilePicture": "https://sessionize.com/image/edd8-400o400o2-DGdpaMrsfMcXUQTgeKxMh1.png", - "sessions": [ + }, { - "id": 530107, - "name": "Threat Landscapes, Threat Models, and Threat Prevention: A Practical Guide for Android Developers" - } - ], - "isTopSpeaker": false, - "links": [ + "title": "Facebook", + "url": "https://www.facebook.com/ayushi2225/", + "linkType": "Facebook" + }, { - "title": "Twitter", - "url": "https://twitter.com/licel_en", - "linkType": "Twitter" + "title": "Instagram", + "url": "https://www.instagram.com/ayushigupta_22/", + "linkType": "Instagram" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/company/licel", - "linkType": "LinkedIn" + "title": "Blog", + "url": "https://www.ayushig.com/blog", + "linkType": "Blog" }, { "title": "Company Website", - "url": "https://licelus.com/", + "url": "https://www.zalando.de/damen-home/", "linkType": "Company_Website" } ], @@ -519,70 +455,63 @@ "categories": [] }, { - "id": "e869461e-b6fb-43e5-a89c-033da87008c0", - "firstName": "Ben", - "lastName": "Weiss", - "fullName": "Ben Weiss", - "bio": "Improving Android Developer's experience, one technology at a time.", - "tagLine": "Android Developer Relations at Google", - "profilePicture": "https://sessionize.com/image/d3c4-400o400o2-GQJu2EdAHAEgFUwBq8bF1o.jpg", + "id": "6b808c1f-836d-4349-9585-a89355dfb8d0", + "firstName": "Balázs", + "lastName": "Gerlei", + "fullName": "Balázs Gerlei", + "bio": "Balázs is a software engineer skilled in software architectural design, mobile security, effective product development and agile methodologies. His passion is creating intuitive, experience-focused user interfaces. He is always hungry for more knowledge and happy to share what he learns with others. He likes mentoring and being an enabler in a team. He is a co-organizer of the Android Budapest meetup group. Outside of work he enjoys cycling, photography, cars and coffee.", + "tagLine": "Senior Mobile Software Engineer @ Nevis Security", + "profilePicture": "https://sessionize.com/image/578f-400o400o1-431603bd-753a-4e59-9fe6-4ed52e29e22e.jpg", "sessions": [ { - "id": 528396, - "name": "Android App Performance in a Nutshell" + "id": 706060, + "name": "Overcoming Unsecurities in WebViews" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/keyboardsurfer", + "title": "X (Twitter)", + "url": "https://twitter.com/balazsgerlei", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/keyboardsurfer", + "url": "https://hu.linkedin.com/in/balazs-gerlei", "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://medium.com/keyboardsurfer", - "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://d.android.com", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "ca54f2c6-836e-41b2-ba24-78870a7c0384", - "firstName": "Benedict", - "lastName": "Kennedy", - "fullName": "Benedict Kennedy", - "bio": "Benedict started as an Android Engineer in Motorola in 2010 and has progressed to found & lead the Front-end Development team in the Innovation & Design organisation in Zebra Technologies.\r\nThe Front-end team specialises in implementing high quality User Experiences for Zebra's range of Android products as well as web & desktop apps.", - "tagLine": "Senior Manager, Front-end Development & User Experience - Zebra Technologies", - "profilePicture": "https://sessionize.com/image/c8d6-400o400o2-HKDE33AgvEgMuVoTogbMX4.jpg", + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "firstName": "Ben", + "lastName": "Sagmoe", + "fullName": "Ben Sagmoe", + "bio": "Ben is a Developer Relations Engineer at Google covering the Android for Cars Space (Android Auto and Android Automotive OS). In the past, he has spoken at the Android Developer Summit and Google I/O connect events.", + "tagLine": "Developer Relations Engineer, Google", + "profilePicture": "https://sessionize.com/image/f693-400o400o1-V5FxHkv47YeFNkZP7YoCgi.jpg", "sessions": [ { - "id": 536951, - "name": "Android Large Screen: how your app can power a workstation" + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/benedict-kennedy/", + "url": "https://www.linkedin.com/in/bsagmoe/", "linkType": "LinkedIn" - }, - { - "title": "Company Website", - "url": "https://www.zebra.com", - "linkType": "Company_Website" } ], "questionAnswers": [], @@ -595,17 +524,17 @@ "fullName": "Benjamin Kadel", "bio": "Developer, Presenter/Teacher, Open source advocate, Ultimate frisbee player & Board game hoarder.\r\nI love the fact that I get to code for a living, but I am also incredibly passionate about sharing, collaborating and helping others on their dev journey!\r\nI also have a growing YouTube channel (youtube.com/benkadel), where I try to make entertaining & educational content about programming, the tech industry and specific tutorials to breakdown complex topics.\r\nI adore presenting, story-telling and just generally engaging with an audience. If I am able to make life, even just a little brighter for someone then I have succeeded!", "tagLine": "Principal Platform Engineer for Android at Babbel", - "profilePicture": "https://sessionize.com/image/caa1-400o400o2-TVKM76Vzy6niGgkteUeC4H.jpg", + "profilePicture": "https://sessionize.com/image/fef1-400o400o1-L7RptkTbWuBP7ayp4xBbN6.jpg", "sessions": [ { - "id": 495137, - "name": "Practical ADB usage to enhance your life!" + "id": 693397, + "name": "Crime Scene InvestiGITor" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/Ben_Kadel", "linkType": "Twitter" }, @@ -629,115 +558,134 @@ "categories": [] }, { - "id": "bbcf99f4-d0a2-4081-afe8-114e09f54a86", - "firstName": "Bob", - "lastName": "Dahlberg", - "fullName": "Bob Dahlberg", - "bio": "I have been a developer since I started my first company in high school. I've built large campaign frameworks, several video players and chats, streaming services, mobile apps, and microservices to support them. Currently, my main language is Kotlin and my focus is mentoring and leading mobile tech teams. Since day one, I've been an asynchronous programmer and was used to working both reactive and functional before it became hip.", - "tagLine": "Kotlin, Android, Lead and trying to be helpful.", - "profilePicture": "https://sessionize.com/image/1465-400o400o2-TuHxs7NTf7J7Eg4PYBfhHf.jpg", + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "firstName": "Bhushan", + "lastName": "Sonawane", + "fullName": "Bhushan Sonawane", + "bio": "Bhushan has optimized and deployed more than 1000s of AI models on-device on iOS and Android ecosystem.\r\nCurrently, He is building AI Hub at Qualcomm to make on-device journey on Android and Snapdragon platform as seamless as possible.\r\nPreviously, he worked on Apple on CoreML framework and helped deployed various system and developer use cases. He also worked at Nvidia in GPU compiler focusing on optimizing code generation for CUDA and graphics load (e.g. Nintendo and Nvidia Shield).", + "tagLine": "Staff ML Engineer, Qualcomm", + "profilePicture": "https://sessionize.com/image/647c-400o400o1-PReFokhoaxSZx9Zgv8b9nE.jpg", "sessions": [ { - "id": 510229, - "name": "Conducting tech interviews" + "id": 748950, + "name": "How to Optimize, Validate and Deploy ML Models On Device" + }, + { + "id": 768482, + "name": "How to Optimize, Validate and Deploy ML Models On Device (Part II)" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/mr_bob", + "title": "X (Twitter)", + "url": "https://x.com/bhushan23s", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/dahlbergbob/", + "url": "https://www.linkedin.com/in/bhushansonawane/", "linkType": "LinkedIn" + }, + { + "title": "Blog", + "url": "https://bhushansonawane.com/", + "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://www.qualcomm.com/", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "c74036f4-aebb-4fae-899a-7fb7cb377d05", - "firstName": "Carl-Gustaf", - "lastName": "Harroch", - "fullName": "Carl-Gustaf Harroch", - "bio": "Carl-Gustaf Harroch, the dynamic leader of Novoda, is known for his strategic foresight and nurturing leadership. Under his guidance, Novoda has soared to international success. A community-builder at heart, Carl also founded Londroid and kickstarted Droidcon London, leaving an indelible mark on the tech landscape.", - "tagLine": "Founder/Managing Director - Novoda ", - "profilePicture": "https://sessionize.com/image/94dc-400o400o2-MgVShGCM3xMo5mvrBeA8Vw.jpg", + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "firstName": "Chiko", + "lastName": "Shimizu", + "fullName": "Chiko Shimizu", + "bio": "Chiko is an Android Developer Relation Engineer at Google, focusing on apps on fascinating surfaces. ", + "tagLine": "Developer Relation Engineer, Android, Google", + "profilePicture": "https://sessionize.com/image/96d8-400o400o1-NtevfpPvBn9sRDfYBgxdKE.jpg", "sessions": [ { - "id": 529143, - "name": "Challenges, failures and lessons in 15 years of building on top of Android." - } - ], - "isTopSpeaker": false, - "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/charroch", - "linkType": "Twitter" - }, - { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/carlharroch/", - "linkType": "LinkedIn" + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" }, { - "title": "Blog", - "url": "http://novoda.com/blog", - "linkType": "Blog" + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" }, { - "title": "Company Website", - "url": "https://novoda.com", - "linkType": "Company_Website" + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "5d3a7bdc-4d73-4985-91f2-a388978732a9", + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", "firstName": "Chris", - "lastName": "Simmonds", - "fullName": "Chris Simmonds", - "bio": "Chris Simmonds is a software consultant and trainer living in southern England. He has two decades of experience in designing and building open-source embedded systems. He is the founder and chief consultant at 2net Ltd, which provides professional training and mentoring services in Android platform development, Embedded Linux, and Linux device drivers. He has trained engineers at many of the biggest companies in the embedded world. He is the author of the book “Mastering Embedded Linux Programming”, and is a frequent presenter at open source and embedded conferences, including the Embedded Linux Conference and FOSDEM. You can see some of his work on the “Inner Penguin” blog at 2net.co.uk.\r\n", - "tagLine": "Looking after the inner penguin", - "profilePicture": "https://sessionize.com/image/491c-400o400o2-dc-4d73-4985-91f2-a388978732a9.4c0bd79a-045c-4f82-8e7f-cb0e1225a865.jpeg", + "lastName": "Assigbe", + "fullName": "Chris Assigbe", + "bio": "Chris is a Developer Relations Engineer on the Android team.\r\nHe is passionate about technology and its intersection with society advancement. \r\nHe is also a huge home automation nerd and can be found tinkering with a new more advanced automation on the weekends.", + "tagLine": "Android Developer Relations Engineer @ Google", + "profilePicture": "https://sessionize.com/image/16b1-400o400o1-WZgwsfPGwJ3DkFnZCfvjtG.jpg", "sessions": [ { - "id": 529953, - "name": "Why can't my app open that file? A deep dive into the Android app sandbox" + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/2net_software", + "url": "https://twitter.com/cka_assi", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://uk.linkedin.com/in/chrisdsimmonds/", + "url": "https://www.linkedin.com/in/chris-koffi-assigbe-07777424/", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://2net.co.uk/blog", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://2net.co.uk", + "url": "https://google.com", "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, + { + "id": "391191b2-1d5c-4784-875b-00edf4d065cc", + "firstName": "Chris", + "lastName": "Assigbe", + "fullName": "Chris Assigbe", + "bio": null, + "tagLine": "Android Developer Relations Engineer at Google", + "profilePicture": "https://sessionize.com/image/7f8b-400o400o1-EeD2rbvys4TkahpNW3icpi.jpg", + "sessions": [ + { + "id": 764378, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, { "id": "eb4483f1-306b-447d-9888-648ec0fc0018", "firstName": "Chris", @@ -745,11 +693,47 @@ "fullName": "Chris Sinco", "bio": "Chris is the UX Design Lead for Android Developer tools. He has a passion for creating elegant developer experiences. Prior to Google, he worked on VS Code and Hololens at Microsoft.", "tagLine": "UX Design Lead @ Google", - "profilePicture": "https://sessionize.com/image/b13c-400o400o2-f1-306b-447d-9888-648ec0fc0018.f1f8f009-2f22-403f-9d74-9ebe5d474da9.jpg", + "profilePicture": "https://sessionize.com/image/3b04-400o400o1-f1-306b-447d-9888-648ec0fc0018.f1f8f009-2f22-403f-9d74-9ebe5d474da9.jpg", "sessions": [ { - "id": 508933, - "name": "Meet Jewel: create IDE plugins in Compose ✨" + "id": 731273, + "name": "Project Sparkles: how Compose is changing Android Studio" + }, + { + "id": 764303, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764328, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764329, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764330, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" + }, + { + "id": 764404, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764406, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764408, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764409, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], "isTopSpeaker": false, @@ -759,6 +743,11 @@ "url": "https://twitter.com/csinco", "linkType": "Twitter" }, + { + "title": "Blog", + "url": "https://csinco.com", + "linkType": "Blog" + }, { "title": "Company Website", "url": "https://developer.android.com", @@ -769,34 +758,39 @@ "categories": [] }, { - "id": "f53882b1-df66-4d6c-9ec1-888badcdd224", - "firstName": "Chris", - "lastName": "Ward", - "fullName": "Chris Ward", - "bio": "Chris started coding life as a full-stack developer and moved between all sorts of weird and wonderful disciplines and industries before he settled on Android. In addition to coding life, he's been an elected official in the UK, had a key role in legalising same-sex marriage in the UK through his LobbyALord app, and has addressed the UN Human Rights Council in Geneva on atrocities against LGBT+ people abroad. Diagnosed with depression, anxiety and ADHD, he taglines himself as Very Mentally Ill(TM) and writes/speaks about his experience of being diagnosed very late in life and how he deals with the unique challenges ADHD brings to an individual working in tech.", - "tagLine": "Engineering Manager, Babbel", - "profilePicture": "https://sessionize.com/image/868e-400o400o2-aoMzS8JtuwKyoXBvoE8scW.png", + "id": "84335047-a7cd-437e-986f-1d6b6df31072", + "firstName": "Chrystian", + "lastName": "Vieyra Cortes", + "fullName": "Chrystian Vieyra Cortes", + "bio": "Chrystian Vieyra is an Android and iOS app developer with a specialty in visualizing sensor data. He recently transitioned to be an engineering manager at Comcast. Born in Celaya, Mexico, he immigrated to the United States to complete his B.S. in Computer Science from Western Illinois University. Chrystian resides in Washington, DC.", + "tagLine": "Engineering Manager", + "profilePicture": "https://sessionize.com/image/e02a-400o400o1-Y27vRVQocc9Y4yhBoqSAEy.jpg", "sessions": [ { - "id": 530186, - "name": "Coding for Good: Achieving social change with an app" + "id": 731303, + "name": "Embracing 16 KB Page Sizes in Android - Boosting Performance" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://www.twitter.com/christopherward", + "title": "X (Twitter)", + "url": "https://twitter.com/chrystianv1", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/chris-ward-88822623/", + "url": "https://www.linkedin.com/in/chrystian-vieyra-4aa1406/", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://www.vieyrasoftware.net/blog", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://babbel.com/", + "url": "https://www.xfinity.com/", "linkType": "Company_Website" } ], @@ -804,39 +798,62 @@ "categories": [] }, { - "id": "f897009d-854d-4403-ab47-44c29d8edce1", - "firstName": "Daniele", - "lastName": "Bonaldo", - "fullName": "Daniele Bonaldo", - "bio": "Daniele is an Android developer passionate about photography, wearable technologies and Internet of Things.\r\nFreelance developer, international conference speaker and Google Developer Expert, now he loves to work on everything related to the Android ecosystem, from Chromecast to Wear to smart mirrors.", - "tagLine": "Android Developer e GDE", - "profilePicture": "https://sessionize.com/image/ead5-400o400o2-a6138775-ab60-488b-98d0-1e1005428fc1.jpg", + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "firstName": "Clara", + "lastName": "Bayarri", + "fullName": "Clara Bayarri", + "bio": null, + "tagLine": "Engineering Manager - Google", + "profilePicture": "https://sessionize.com/image/a223-400o400o1-PnXFpX838sUVUza8x8KdsB.jpg", + "sessions": [ + { + "id": 771426, + "name": "#TheAndroidShow - Leadership Keynote" + }, + { + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "6ee5ee5a-1bdd-4c54-8178-fad51f86a78a", + "firstName": "Daniel", + "lastName": "Horowitz", + "fullName": "Daniel Horowitz", + "bio": "I'm software crafter passionate about mobile tech and AI.\r\n\r\nM.Sc. in Artificial Intelligence at Universidad Politecnica de Catalunia (Barcelona) and Android Architect/Team leader. Working on Android development since 2011", + "tagLine": "Android @ Spotify", + "profilePicture": "https://sessionize.com/image/4af9-400o400o1-136a8c8e-6cd5-47df-b9d6-aecdf4176121.jpg", "sessions": [ { - "id": 530595, - "name": "Sink or swim: proving ideas in production, fast" + "id": 710572, + "name": "Rethinking Home - How testing techniques and code design reshaped the new Spotify Home feature" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/danybony_", + "title": "X (Twitter)", + "url": "https://twitter.com/horowitzd", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/danielebonaldo", + "url": "https://www.linkedin.com/in/danihorowitz/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://www.danielebonaldo.com", + "url": "https://danielhorowitz.io", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://www.danielebonaldo.com", + "url": "https://spotify.com", "linkType": "Company_Website" } ], @@ -844,39 +861,34 @@ "categories": [] }, { - "id": "29ba7916-20b4-48e5-9715-bddfbdd1aa3a", - "firstName": "Dominik", - "lastName": "Roszkowski", - "fullName": "Dominik Roszkowski", - "bio": "Dominik is Google Developer Expert in Flutter eager to create beautiful apps for mobile and desktop. He’s Lead Engineer at Visible and co-founder of Flutter Europe conference. He is active speaker on local meetups and writes Flutter-related articles on his blog roszkowski.dev.", - "tagLine": "Google Developer Expert in Flutter", - "profilePicture": "https://sessionize.com/image/e8e9-400o400o2-VHDtKbD3b8L3R7aA5ZyDET.jpeg", + "id": "de4da391-af75-405b-8816-186c5d4e8fde", + "firstName": "Daniel", + "lastName": "Neamtu", + "fullName": "Daniel Neamtu", + "bio": "Daniel Neamtu is the EMEA Developer Advocate for Zebra Technologies.\r\n\r\nDaniel has a good Android Development experience with over 6 years of work on APPs and Modding/Custom ROMs projects.\r\nHe understands and is passionate about helping the developers improving their skills and getting the most out of it.", + "tagLine": "Developer Advocate", + "profilePicture": "https://sessionize.com/image/e662-400o400o1-Knzz3ERhXW1uUVcCTpHj5w.jpg", "sessions": [ { - "id": 528157, - "name": "Offline First Architecture with Bloc and Reactive Database" + "id": 732898, + "name": "Your app could use a secondary screen!" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/OrestesGaolin", + "title": "X (Twitter)", + "url": "https://twitter.com/nilac_", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/dominik-roszkowski/", + "url": "https://www.linkedin.com/in/nilac/", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://roszkowski.dev", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://makevisible.com", + "url": "https://www.zebra.com/gb/en.html", "linkType": "Company_Website" } ], @@ -884,39 +896,39 @@ "categories": [] }, { - "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", - "firstName": "Ed", - "lastName": "Holloway-George", - "fullName": "Ed Holloway-George", - "bio": "Ed Holloway-George is an Android Developer and Google Developer Expert originally from Oxford, UK but now currently residing in Nottingham, UK.\r\n\r\nAn Android developer for nearly 10 years; Ed now works for ASOS as a Senior Developer having previously worked on well-known applications such as National Trust, My Oxfam, Snoop, Carling Tap and many more.\r\n\r\nIn his spare time, Ed can be found tweeting and posting pictures of his dog.", - "tagLine": "Senior Android Developer @ ASOS | Android GDE", - "profilePicture": "https://sessionize.com/image/9006-400o400o2-Suu1CSFCXHg2BNnxnvBxHE.png", + "id": "9c8384de-140e-47e4-aeb1-c85faf090fc8", + "firstName": "Danielle", + "lastName": "Vass", + "fullName": "Danielle Vass", + "bio": "Danielle is an experienced mobile developer and has contributed to several Android and iOS apps and SDKs, for employers and her own personal projects. She is passionate about helping to improve diversity and inclusion in the technology industry and has run many activities for children and adults to learn to code.", + "tagLine": "de-velopment", + "profilePicture": "https://sessionize.com/image/9743-400o400o1-DrNzd82APp1kgSrmaTj61Q.jpeg", "sessions": [ { - "id": 495134, - "name": "How to stop the ‘Gradle Snatchers’: Securing your builds from baddies" + "id": 736123, + "name": "How we build Spotify for Android Automotive" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/Sp4ghettiCode", + "title": "X (Twitter)", + "url": "https://twitter.com/de_velopment", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/edgeorge", + "url": "https://www.linkedin.com/in/daniellevass/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://spght.dev", + "url": "https://danielle.ai/", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://spght.dev", + "url": "http://www.de-velopment.com/", "linkType": "Company_Website" } ], @@ -924,39 +936,39 @@ "categories": [] }, { - "id": "48a744a3-c5e5-40b7-8c64-eb134ff4dee7", - "firstName": "Edward", - "lastName": "Harker", - "fullName": "Edward Harker", - "bio": "Edward is an Android software engineer at Deliveroo. He cares about building high quality apps, testing and performance. ", - "tagLine": "Android software engineer at Deliveroo", - "profilePicture": "https://sessionize.com/image/3d65-400o400o2-fzrHZNnTm2bYxoeeFmgUCb.jpg", + "id": "4d3a31b1-61e5-4a26-9ca3-c9f30e3eaa08", + "firstName": "Danny", + "lastName": "Preussler", + "fullName": "Danny Preussler", + "bio": "Danny built mobile applications for companies like Viacom, Groupon, eBay and Alcatel. He is a Google Developer Expert for Android and Kotlin. Before there was Android he was already active in the Blackberry development community.", + "tagLine": "Android Lead @ Soundcloud", + "profilePicture": "https://sessionize.com/image/b921-400o400o1-KQg41ScvicfHUFfnJxgvmA.jpg", "sessions": [ { - "id": 529488, - "name": "A/B Test Best Practices and Fun Case Studies from Deliveroo" + "id": 700492, + "name": "Ten things you heard about testing that might be wrong" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/edwardharks", + "title": "X (Twitter)", + "url": "https://twitter.com/PreusslerBerlin", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "http://www.linkedin.com/in/edward-harker-83984950", + "url": "https://www.linkedin.com/in/danny-preussler-557a3b79/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://edwardharker.com/", + "url": "https://medium.com/@dpreussler", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://deliveroo.co.uk/", + "url": "http://www.soundcloud.com", "linkType": "Company_Website" } ], @@ -964,29 +976,104 @@ "categories": [] }, { - "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", - "firstName": "Eeva-Jonna", - "lastName": "Panula", - "fullName": "Eeva-Jonna Panula", - "bio": "Eeva-Jonna, or Eevis, is an accessibility specialist and senior Android developer. She is also IAAP Certified Professional in Web Accessibility and Women Techmakers Ambassador. Her passion is to share knowledge, and she speaks and writes a blog. When she is not doing that, she explores the beautiful Finnish nature either by foot or kayak.", - "tagLine": "Accessibility Specialist, Senior Android Developer at Oura", - "profilePicture": "https://sessionize.com/image/7670-400o400o2-J3UgjVGJhA1e8k31Lz9Fkc.jpg", + "id": "81f9ecbf-c08c-48c7-850f-1e79e1439867", + "firstName": "David", + "lastName": "Ta", + "fullName": "David Ta", + "bio": "Staff Software Engineer at Decathlon · GDG Nantes Android Lead · AndroidMakers by droidcon Staff · Watercolorist", + "tagLine": "Staff Software Engineer at Decathlon · GDG Nantes Android Lead", + "profilePicture": "https://sessionize.com/image/6b18-400o400o1-ffkvKCN7FtT2fJ2Cp8UEZ8.png", "sessions": [ { - "id": 518266, - "name": "Making Data Visualizations More Accessible - Lessons Learned" + "id": 720685, + "name": "Why is adaptive layout a nightmare?" } ], "isTopSpeaker": false, "links": [ + { + "title": "Twitter", + "url": "https://twitter.com/Smoochibi", + "linkType": "Twitter" + }, { "title": "LinkedIn", - "url": "https://linkedin.com/in/eevajonna", + "url": "https://www.linkedin.com/in/david-ta-63524b53/", + "linkType": "LinkedIn" + }, + { + "title": "Company Website", + "url": "https://www.decathlon.com", + "linkType": "Company_Website" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "f589d95b-8262-4337-b6c8-78b4701afa3a", + "firstName": "Dean", + "lastName": "Djermanović", + "fullName": "Dean Djermanović", + "bio": "Dean is an Android Engineer based in Zagreb, Croatia. He has been working with Android for 7 years. He's currently employed at Endava. Previously, Dean was part of FIVE, a company recognized as a Google Developers Certified Agency in 2018, which was later acquired by Endava. He's an active member of the Android community. He's been a part of the kodeco.com tutorial team for 5 years where he ​​contributed to three Android book projects and numerous tutorials. He also participates as a speaker at tech conferences and local tech meetups. In his free time, he loves biking, and working out in the gym.", + "tagLine": "Android Engineer at Endava", + "profilePicture": "https://sessionize.com/image/2864-400o400o1-osD3u84jvVv4WzyNQir7N.jpg", + "sessions": [ + { + "id": 732470, + "name": "Exploring Kotlin Symbol Processing: A Practical Guide" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "X (Twitter)", + "url": "https://twitter.com/dean_95_", + "linkType": "Twitter" + }, + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/deandjermanovic/", + "linkType": "LinkedIn" + }, + { + "title": "Company Website", + "url": "https://five.agency/", + "linkType": "Company_Website" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "c24d26ff-8a59-4954-b768-fcd09d8bb0a3", + "firstName": "Domen", + "lastName": "Lanišnik", + "fullName": "Domen Lanišnik", + "bio": "Experienced Senior Android Engineer with over 9 years of experience developing large-scale mobile applications. Currently working as an Android Engineer at Lyft. Actively contributing to the developer community through technical blog posts, mentoring, and talks.", + "tagLine": "Senior Android Developer, Lyft", + "profilePicture": "https://sessionize.com/image/7daa-400o400o1-EZxytWtcJrC8eyrxcvNopQ.jpg", + "sessions": [ + { + "id": 716104, + "name": "Swift Cheatsheet for Android/Kotlin Developers" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "X (Twitter)", + "url": "https://twitter.com/DomenLanisnik", + "linkType": "Twitter" + }, + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/domenlanisnik/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://eevis.codes/blog", + "url": "https://medium.com/@domen.lanisnik", "linkType": "Blog" } ], @@ -994,63 +1081,112 @@ "categories": [] }, { - "id": "758dff56-ba98-4d4d-ab60-87472b2b4846", - "firstName": "Efthymios", - "lastName": "Sarmpanis", - "fullName": "Efthymios Sarmpanis", - "bio": "I have over 16 years of experience, in different organizations leading teams.\r\n\r\nI am a GDE for Dart & Flutter and am also the founder of the Flutter Greek Community meetup and the creator of flutterings packages (https://github.com/flutterings).\r\n\r\nI have been delivering talks about app architecture and design for 2 years.", - "tagLine": "GDE Dart & Flutter, CTO at OneRoof", - "profilePicture": "https://sessionize.com/image/33c7-400o400o2-0ed2b4f5-ab60-4872-9cdf-0278f7bc8d71.jpg", + "id": "31e8a089-eb2f-437c-ac09-bb55af17f934", + "firstName": "Donovan", + "lastName": "McMurray", + "fullName": "Donovan McMurray", + "bio": null, + "tagLine": "Developer Relations Engineer", + "profilePicture": "https://sessionize.com/image/c438-400o400o1-hYZVULJxP25W4tzy4rgArU.jpg", "sessions": [ { - "id": 521242, - "name": "How not to ship an app. Lessons and experiences shipping bugs to production." + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", + "firstName": "Ed", + "lastName": "Holloway-George", + "fullName": "Ed Holloway-George", + "bio": "Ed Holloway-George is an Android Developer and Google Developer Expert originally from Oxford, UK but now currently residing in Nottingham, UK.\r\n\r\nAn Android developer for over 10 years; Ed now works for ASOS as a Lead Developer having previously worked on well-known applications such as National Trust, My Oxfam, Snoop, Carling Tap and many more.\r\n\r\nIn his spare time, Ed can be found tweeting and posting pictures of his dog.", + "tagLine": "Lead Android Developer @ ASOS | Android GDE", + "profilePicture": "https://sessionize.com/image/f620-400o400o1-Suu1CSFCXHg2BNnxnvBxHE.png", + "sessions": [ + { + "id": 702107, + "name": "[REDACTED]: How to keep your app's secrets, secret" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/efthemess", + "title": "X (Twitter)", + "url": "https://twitter.com/Sp4ghettiCode", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/efthymis/", + "url": "https://www.linkedin.com/in/edgeorge", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://efthymis.com", + "url": "https://spght.dev", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://oneroofapp.com", + "url": "https://spght.dev", "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, + { + "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", + "firstName": "Eeva-Jonna", + "lastName": "Panula", + "fullName": "Eeva-Jonna Panula", + "bio": "Eeva-Jonna, or Eevis, is an accessibility specialist and senior Android developer. She is also Android GDE and Women Techmakers Ambassador. Her passion is to share knowledge, and she speaks and writes a blog. When she is not doing that, she explores the beautiful Finnish nature either by foot or kayak.", + "tagLine": "Accessibility Specialist, Senior Android Developer at ŌURA", + "profilePicture": "https://sessionize.com/image/5730-400o400o1-SVVjQhkgm8NTQhUofxixAd.png", + "sessions": [ + { + "id": 711918, + "name": "Personalizing Accessibility" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "LinkedIn", + "url": "https://linkedin.com/in/eevajonna", + "linkType": "LinkedIn" + }, + { + "title": "Blog", + "url": "https://eevis.codes/blog", + "linkType": "Blog" + } + ], + "questionAnswers": [], + "categories": [] + }, { "id": "95a6035d-1167-4cc1-9974-bc065a077893", "firstName": "Enrique", "lastName": "Ramírez", "fullName": "Enrique Ramírez", - "bio": "Android Engineer old enough to remember ActionBarSherlock", + "bio": "Android dev old enough to remember ActionBarSherlock", "tagLine": "Software Engineer at Tesco", - "profilePicture": "https://sessionize.com/image/bd68-400o400o2-5d-1167-4cc1-9974-bc065a077893.a1abe602-5198-4b6d-bea4-3be389382316.jpeg", + "profilePicture": "https://sessionize.com/image/e416-400o400o1-5d-1167-4cc1-9974-bc065a077893.a1abe602-5198-4b6d-bea4-3be389382316.jpeg", "sessions": [ { - "id": 516527, - "name": "The terminal is your friend" + "id": 693733, + "name": "Hardware development with KMP and Compose Desktop" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/kikermo", "linkType": "Twitter" }, @@ -1078,19 +1214,19 @@ "firstName": "Erik", "lastName": "Hellman", "fullName": "Erik Hellman", - "bio": "Erik was raised in the dark woods of Sweden, where the was nothing to do but to learn how to write code for PCs. After many years of suffering through experiences of early C++, Visual basic, Delphi, and every version of Java and C#, he has now found Kotlin and feel comfortable that the future for software is bright again.", - "tagLine": "Severely colourblind, coffee addicted, mistake driven developer.", - "profilePicture": "https://sessionize.com/image/ec6f-400o400o2-93-1b16-4a2b-b0a9-ea4cd25b92f5.4cc61ad2-1d12-4dab-81b4-6c2818bbe6de.jpg", + "bio": "With more than 25 years of writing software, and 16 working on Android in various forms, Erik has plenty of experience to share. He lives and works as a freelanced software developer from his home in Stockholm, Sweden. When he's not working he is unsuccessfully trying to herd his two cats or trying to figure out how to maintain his house.", + "tagLine": "Freelance software engineer", + "profilePicture": "https://sessionize.com/image/d66a-400o400o1-93-1b16-4a2b-b0a9-ea4cd25b92f5.4cc61ad2-1d12-4dab-81b4-6c2818bbe6de.jpg", "sessions": [ { - "id": 524023, - "name": "Working with custom Android devices" + "id": 731970, + "name": "Wireless protocols for the next generation IoT devices" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/ErikHellman", "linkType": "Twitter" }, @@ -1114,39 +1250,34 @@ "categories": [] }, { - "id": "67c6035b-3961-48b0-b4f8-49593995c5a0", - "firstName": "Etienne", - "lastName": "Studer", - "fullName": "Etienne Studer", - "bio": "Etienne Studer is the SVP of Engineering, Develocity, at Gradle Inc. He leads the software engineering team behind Develocity, the leading technology-enabling platform for the practice of Developer Productivity Engineering (DPE). As the founding team lead of Develocity since 2015, Etienne has successfully scaled and guided a fully distributed team of engineers across the globe that develops and delivers innovative technologies to improve developer productivity dramatically and transform the developer experience. Before joining Gradle, Etienne was CTO at edorasware, overseeing product and company growth for the intelligent business automation platform. He is based in the region of Basel, Switzerland, and holds a Master’s in Molecular Biology from the University of Basel.\r\n\r\n\r\n", - "tagLine": "SVP of Engineering", - "profilePicture": "https://sessionize.com/image/c665-400o400o2-HC53pzQyQRMfyVqpCQGRK8.jpg", + "id": "d2ddc9e3-35b6-44a9-8daa-144d89cabaff", + "firstName": "Evgeny", + "lastName": "Mandrikov", + "fullName": "Evgeny Mandrikov", + "bio": "Evgeny is a contributor to various open source projects, including OpenJDK, and a speaker at international conferences and JUGs. He is also one of the project leads of widely adopted code coverage library for Java and Kotlin - JaCoCo, and the award-winning EclEmma project at the Eclipse Foundation that integrates JaCoCo into Eclipse. In his day job at Sonar he develops SonarQube’s static source code analysis for languages such as Java, Kotlin, C, and C++.", + "tagLine": "Software Gardener, Language Team Technical Leader @SonarSource", + "profilePicture": "https://sessionize.com/image/6f13-400o400o1-fWhP66qq2SDdw33AoPHXsD.jpg", "sessions": [ { - "id": 528351, - "name": "Becoming and Staying a Productive Developer with Build Scans, Build Validation Scripts and Gradle" + "id": 704275, + "name": "The state of code coverage for Kotlin" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/etiennestuder", + "url": "https://twitter.com/_Godin_", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/etiennestuder", + "url": "https://linkedin.com/in/mandrikov", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://blog.gradle.com", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://gradle.com", + "url": "https://sonarsource.com/", "linkType": "Company_Website" } ], @@ -1154,30 +1285,117 @@ "categories": [] }, { - "id": "0e28eaf0-f8b6-4475-a3be-f3ce49f2d1ec", - "firstName": "Fanny", - "lastName": "Demey", - "fullName": "Fanny Demey", - "bio": "Fanny is a mobile developer and an accessibility expert. She cares about people and the planet and decided 3 years ago to work exclusively on tech for good project, to use her technical skills to make a better world.\r\nShe is also involved in the French tech community as she is the main leader of the GDG Lille group and one of the organizer of the French Android user group. ", - "tagLine": "Mobile Engineer / Accessibility expert - Freelance ", - "profilePicture": "https://sessionize.com/image/fadf-400o400o2-3MzqkXyeRP8iq15ZThRqY4.jpg", + "id": "752bc716-f1b8-45d4-b0a5-cfbc3402b68b", + "firstName": "Florina", + "lastName": "Muntenescu", + "fullName": "Florina Muntenescu", + "bio": null, + "tagLine": "Engineering Manager @ Google", + "profilePicture": "https://sessionize.com/image/e6c6-400o400o1-QBFfoB8esF3yB3Qm1aVZKG.jpg", "sessions": [ { - "id": 504429, - "name": "Deep-Dive Into Mobile Accessibility & Interactive Quiz" + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" } ], "isTopSpeaker": false, - "links": [ + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "firstName": "Francesco", + "lastName": "Romano", + "fullName": "Francesco Romano", + "bio": null, + "tagLine": "Developer Relations Engineer", + "profilePicture": "https://sessionize.com/image/8e65-400o400o1-ajN8q372RqsRcnkzkKrCdF.jpg", + "sessions": [ { - "title": "Twitter", - "url": "https://twitter.com/FannyDemey", - "linkType": "Twitter" + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "firstName": "Fung", + "lastName": "Lam", + "fullName": "Fung Lam", + "bio": "Fung is a Developer Relations Engineer in Android. He focuses on App Compatibility for more than 5 years, helping Android Developers to make their apps ready for the future releases of Android.", + "tagLine": "Developer Relations Engineer, Google", + "profilePicture": "https://sessionize.com/image/85c5-400o400o1-YZexnpvkTSr3zmwvrNqPa6.jpg", + "sessions": [ + { + "id": 756374, + "name": "Tools that could help you to target newer SDK level and improve compatibility" + }, + { + "id": 764345, + "name": "Ask Android! - Compose, GenAI, Platform" + }, + { + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" }, + { + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764397, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764398, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764399, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764400, + "name": "Ask Android! - Compose, WearOS, Platform" + } + ], + "isTopSpeaker": false, + "links": [ { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/fannydemey", + "url": "https://www.linkedin.com/in/lamchofung/", "linkType": "LinkedIn" + }, + { + "title": "Instagram", + "url": "https://www.instagram.com/androidfung", + "linkType": "Instagram" + }, + { + "title": "Company Website", + "url": "https://d.android.com", + "linkType": "Company_Website" } ], "questionAnswers": [], @@ -1190,17 +1408,17 @@ "fullName": "Gerard Paligot", "bio": "Father and husband · Google Developer Expert for Android · Mobile Staff Engineer at Decathlon · GDG Lille (Android), FRAUG and Devfest Lille organizer · Disney Fan!", "tagLine": "Decathlon, Mobile Staff Engineer", - "profilePicture": "https://sessionize.com/image/b304-400o400o2-WVYBKj5yMs9sFKE3h5QzQd.png", + "profilePicture": "https://sessionize.com/image/c5a4-400o400o1-sqz6c5v3xty7GXR2UD1HiR.png", "sessions": [ { - "id": 502207, - "name": "Take your shot of Vitamin!" + "id": 720685, + "name": "Why is adaptive layout a nightmare?" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/GerardPaligot", "linkType": "Twitter" }, @@ -1216,7 +1434,7 @@ }, { "title": "Company Website", - "url": "https://technology.decathlon.com/", + "url": "https://digital.decathlon.net/", "linkType": "Company_Website" } ], @@ -1224,104 +1442,94 @@ "categories": [] }, { - "id": "106dc281-fe8b-40ac-b16b-240832b27b47", - "firstName": "Giovanni", - "lastName": "Laquidara", - "fullName": "Giovanni Laquidara", - "bio": "Giovanni is Sr. Developer Advocate at Amazon, where he works on supporting developers around the world using the Amazon Appstore on different devices. He previously worked as a software engineer in mobile, real-time defence system and VR/AR. He is Interested in low level programming, IoT, and command line :D.", - "tagLine": "Sr Developer Evangelist @ Amazon", - "profilePicture": "https://sessionize.com/image/58e6-400o400o2-TeUM2YLLfGHpcjVR4LQGn7.png", + "id": "36110e7b-c926-48ec-be43-b1a10e0641a0", + "firstName": "Gil", + "lastName": "Hartman", + "fullName": "Gil Hartman", + "bio": "Mobile Security Engineer with 12+ years of hands-on experience, now leading Appdome Engineering into improving our unified mobile app defense platform", + "tagLine": "VP Engineering, Appdome", + "profilePicture": "https://sessionize.com/image/a7b3-400o400o1-J1SGxaJW2bmavbxsdPmizG.png", "sessions": [ { - "id": 530249, - "name": "Creating Engaging Android TV and Fire TV Apps with Native and Cross-Platform Tools" + "id": 733000, + "name": "Exploring Android Accessibility Malware" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/joaolaq", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/glaquidara/", + "url": "https://www.linkedin.com/in/gil-hartman/", "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://giolaq.dev/", - "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://developer.amazon.com/apps-and-games/blogs", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "5c3850ee-778a-4904-bd52-a3927ec54dc6", - "firstName": "Hakan", - "lastName": "Bagci", - "fullName": "Hakan Bagci", - "bio": "Hakan Bagci is an Android Staff Engineer at Delivery Hero, a food delivery company. He started Android development in 2012 and has 17 years of software development experience. He got his PhD degree in Computer Engineering. After leaving academic path, he has become a passionate Mobile Engineer and published 10+ small sized indie mobile games to both Android and iOS platforms. ", - "tagLine": "Delivery Hero, Principal Android Engineer", - "profilePicture": "https://sessionize.com/image/43c4-400o400o2-S4Rn9nA6xWx5u5ahPXLEQ6.jpg", + "id": "4b141461-befd-4bd3-83c9-1c1f7599a9c6", + "firstName": "Goran", + "lastName": "Minov", + "fullName": "Goran Minov", + "bio": "Goran Minov is a native Macedonian currently living in London (UK), holding a Master of information science in the area of software engineering. He is a Technical Team Lead at Okta and is passionate about Android, cloud technologies and infrastructure engineering.", + "tagLine": "Technical Team Lead @Okta", + "profilePicture": "https://sessionize.com/image/98d0-400o400o1-aGC4JhZ85aShjAmY4TNPT2.jpg", "sessions": [ { - "id": 495541, - "name": "Composing ViewModels: Breaking ViewModels into smaller self-contained UI models" + "id": 756181, + "name": "Fortify the Fort: Think outside the security" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/hknbgcdev", + "title": "X (Twitter)", + "url": "https://twitter.com/lineargs", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/hkn-bgc", + "url": "https://www.linkedin.com/in/lineargs/", "linkType": "LinkedIn" - }, - { - "title": "Company Website", - "url": "https://www.deliveryhero.com/", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "b44c9f22-4d83-4246-bf67-bdd30af9bfef", - "firstName": "Haroldo", - "lastName": "Olivieri", - "fullName": "Haroldo Olivieri", - "bio": "Haroldo invested over a decade of his life in building unique mobile experiences. \r\n\r\nCurrently, he's a Principal Mobile Engineer at Skyscanner, and one of the main challenges of his role is to nurture a culture of tradeoff analysis over technical bias.", - "tagLine": "Principal Engineer @ Skyscanner", - "profilePicture": "https://sessionize.com/image/d2b5-400o400o2-XeNGrPE1Sg7GLYXxPXvzUL.png", + "id": "e11a53fd-1cfb-4930-9dfa-42187e94d869", + "firstName": "Hadi", + "lastName": "Tok", + "fullName": "Hadi Tok", + "bio": "Hadi Develops Android Applications since Android 1.6 Donut and very passionate about it. Developed a handful of Android applications since then with a variety of technical challenges. Hadi currently works at WhatsApp with interest in UI performance. ", + "tagLine": "Android GDE, Software Engineer at WhatsApp", + "profilePicture": "https://sessionize.com/image/365d-400o400o1-M6bTfdGhZo9z73ngPM9Wyg.jpg", "sessions": [ { - "id": 529650, - "name": "Honest mistakes caused by shallow interpretation of SOLID" + "id": 734218, + "name": "Why WhatsApp uses ListView" } ], "isTopSpeaker": false, "links": [ + { + "title": "X (Twitter)", + "url": "https://twitter.com/hadi_tok", + "linkType": "Twitter" + }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/olivierisoares/", + "url": "https://www.linkedin.com/in/haditok/", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://medium.com/@oshamahue", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "http://www.skyscanner.net", + "url": "https://whatsapp.com", "linkType": "Company_Website" } ], @@ -1329,104 +1537,119 @@ "categories": [] }, { - "id": "5b17491b-317a-4fec-8b63-3264bc0a9cb9", - "firstName": "Hudson", - "lastName": "Miears", - "fullName": "Hudson Miears", - "bio": "Working on the Android team at Esri for 3 years, helping to design and build the ArcGIS Maps SDK for Kotlin. Loves code architecture, exploring new technologies, and staring at cool maps. He/him", - "tagLine": "Engineer, ArcGIS Maps SDK for Kotlin Team @ Esri", - "profilePicture": "https://sessionize.com/image/4ad9-400o400o2-a6ugoqTRbpbJZm9UJ5JYwH.jpg", + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "firstName": "Halil", + "lastName": "Ozercan", + "fullName": "Halil Ozercan", + "bio": "Halil Ozercan works in the Compose Text team, mainly focusing on Text APIs like TextFields, text styling, and performance. ", + "tagLine": "Software Engineer – Google", + "profilePicture": "https://sessionize.com/image/1546-400o400o1-ZNPXNnev2CFeRRhbZXANk.jpg", "sessions": [ { - "id": 516305, - "name": "Designing for Errors in a Kotlin-first SDK: When to throw, nullify, or return a Result." + "id": 764343, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764345, + "name": "Ask Android! - Compose, GenAI, Platform" + }, + { + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764400, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764404, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], "isTopSpeaker": false, "links": [ { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/hudson-miears-796bbb121/", - "linkType": "LinkedIn" - }, - { - "title": "Company Website", - "url": "https://developers.arcgis.com/", - "linkType": "Company_Website" + "title": "X (Twitter)", + "url": "https://twitter.com/halilozercan", + "linkType": "Twitter" } ], "questionAnswers": [], "categories": [] }, { - "id": "e98ce7fa-cb48-4eb6-8a60-be9d4b4c0b6a", - "firstName": "Ivan", - "lastName": "Kinash", - "fullName": "Ivan Kinash", - "bio": "Ivan is the CEO and co-founder of Licel, the global app security company. His aim is to help businesses make the digital world safer for their customers.", - "tagLine": "CEO, Licel", - "profilePicture": "https://sessionize.com/image/44cd-400o400o2-p7EJHpENdypaQgzjfVMQip.jpeg", + "id": "0233c2ee-3bcd-4dee-9de5-c765b8c27cc0", + "firstName": "Harriet", + "lastName": "Taylor", + "fullName": "Harriet Taylor", + "bio": "As a seasoned app developer with over 7 years experience, Harriet has been integral in the creation of the android app for the UK's top automotive market place, Auto Trader. With a masters degree in electronic engineering and a focus on innovation, she specialises in user-centric solutions and proof of concepts, both client and server side, using Java & Kotlin. She is dedicated to promoting diversity in technology, particularly empowering women in tech through advocacy and inclusive environments.", + "tagLine": "Senior Android Developer at Auto Trader UK", + "profilePicture": "https://sessionize.com/image/3e50-400o400o1-WsfviTcEnysbcncgX5Qwze.jpg", "sessions": [ { - "id": 530107, - "name": "Threat Landscapes, Threat Models, and Threat Prevention: A Practical Guide for Android Developers" + "id": 708032, + "name": "DIY Server-Driven UI: Building Auto Trader's SDUI platform" } ], "isTopSpeaker": false, "links": [ { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/ivan-kinash-7b668711/", + "url": "https://www.linkedin.com/in/harriettaylor93/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://www.linkedin.com/in/ivan-kinash-7b668711/recent-activity/articles/", + "url": "https://linktr.ee/HarrietInTech", "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://licelus.com", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "83d33e86-8cca-44a8-b0dc-2994332ef8e8", - "firstName": "Ivan", - "lastName": "Morgillo", - "fullName": "Ivan Morgillo", - "bio": "Passionate developer, speaker, writer, and 1/2 of Code with the Italians", - "tagLine": "Android consultant and trainer", - "profilePicture": "https://sessionize.com/image/a1ad-400o400o2-naywFzeWrTQEotymiViL2A.jpg", + "id": "2d41d3fb-3321-4b47-a401-ae84a5de2423", + "firstName": "Huyen Tue", + "lastName": "Dao", + "fullName": "Huyen Tue Dao", + "bio": "Huyen Tue Dao is an Android developer and Google Developer Expert for Android and Kotlin. She previously worked as an Android developer at Trello and Adobe. Huyen also co-hosts the \"Android Faithful\" podcast as part of the Daily Tech News Show network.\r\n\r\nHuyen lives in Denver, CO though is often found in the DC Metro area. When not programming, she is moving (kettlebells, HIIT, dance, yoga, strength) and gaming (video, board, card, mobile, anything).", + "tagLine": "Randomly Typing | Android + Kotlin Google Developer Expert ", + "profilePicture": "https://sessionize.com/image/65aa-400o400o1-n2AHF6pBXM6ML5n55i7UE9.jpg", "sessions": [ { - "id": 530595, - "name": "Sink or swim: proving ideas in production, fast" + "id": 736477, + "name": "Creating a Custom Compose Layout, Step-by-Step" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/hamen", + "title": "X (Twitter)", + "url": "https://twitter.com/queencodemonkey", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/ivanmorgillo/", + "url": "https://www.linkedin.com/in/huyentuedao", "linkType": "LinkedIn" }, + { + "title": "Instagram", + "url": "https://instagram.com/queencodemonkey", + "linkType": "Instagram" + }, { "title": "Blog", - "url": "https://ivanmorgillo.com", + "url": "https://www.randomlytyping.com/", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://ivanmorgillo.com", + "url": "https://www.randomlytyping.com", "linkType": "Company_Website" } ], @@ -1434,114 +1657,128 @@ "categories": [] }, { - "id": "5dd21448-f468-44ad-9220-75d25e8e2370", - "firstName": "James", - "lastName": "Hamilton", - "fullName": "James Hamilton", - "bio": "I'm a compiler engineer at Guardsquare, the creators of ProGuard. I work mainly on ProGuard, ProGuardCORE and the Android security solution DexGuard.\r\n\r\nI previously worked at CERN in Geneva, and before that obtained a PhD in Computer Science focusing on code analysis from Goldsmiths, University of London.", - "tagLine": "Compiler Engineer @ Guardsquare", - "profilePicture": "https://sessionize.com/image/5116-400o400o2-e2a4ef51-464c-4c73-8ac8-4a3333fa12ff.jpg", + "id": "6624cb6c-7fc4-4495-a87f-8060e88d7d18", + "firstName": "Jack", + "lastName": "Adams", + "fullName": "Jack Adams", + "bio": "I’m a Senior Android Engineer at Trainline, with 4 years experience in Android development. I’m passionate about building scalable architectures and processes to enable building apps within large teams. Outside of work, I enjoy Muay Thai and chess as well as keeping up with the latest news in the rocket industry.", + "tagLine": "Senion Android Engineer at Trainline", + "profilePicture": "https://sessionize.com/image/c46b-400o400o1-7GgvUJAM5wAcFiyrHLAzdc.jpg", "sessions": [ { - "id": 530078, - "name": "Think outside the server" + "id": 734509, + "name": "Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/jag_hamilton", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/mrjameshamilton", + "url": "https://www.linkedin.com/in/jack-adams-444874220", "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://jameshamilton.eu/", - "linkType": "Blog" } ], "questionAnswers": [], "categories": [] }, { - "id": "0ef1e55b-29b9-4c4b-938d-c59b6c8ea9d3", - "firstName": "Jamie", - "lastName": "Adkins", - "fullName": "Jamie Adkins", - "bio": "Jamie is an Android Developer at Deliveroo, working on home + menus in the consumer app.\r\n\r\nBefore joining Deliveroo, Jamie worked on Android apps for sports organisations, including the Cricket and Rugby World Cups.", - "tagLine": "Android @ Deliveroo", - "profilePicture": "https://sessionize.com/image/31b9-400o400o2-0469c660-e022-4d59-a05a-81f66c67cc89.jpg", + "id": "0e4f3abe-cc6c-409a-8f34-e346f23c0cc4", + "firstName": "Jeffrey", + "lastName": "Bunn", + "fullName": "Jeffrey Bunn", + "bio": "I'm the co-founder of Clearful, an award-winning multi-platform digital journal for personal growth, and Mealime, a top meal-planning app that was acquired in 2018. Alongside my wife Maria, we create consumer products that enrich lives. I also support developers at RevenueCat, the top app monetization platform.", + "tagLine": "Co-Founder of Clearful and Mealime & Support Engineer at RevenueCat", + "profilePicture": "https://sessionize.com/image/ee03-400o400o1-pyJXN76ig64L6grc7BHKmh.png", "sessions": [ { - "id": 529488, - "name": "A/B Test Best Practices and Fun Case Studies from Deliveroo" + "id": 741888, + "name": "Monetizing Your Side Project to $1k in Monthly Revenue and Beyond" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/jamieadkins95", + "title": "X (Twitter)", + "url": "https://twitter.com/jeffreybunn", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/jamieadkins95/", + "url": "https://linkedin.com/in/jeffrey-bunn", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://medium.com/@jamieadkins95", + "url": "https://www.jeffreybunn.com/", "linkType": "Blog" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "firstName": "Jelle", + "lastName": "Fresen", + "fullName": "Jelle Fresen", + "bio": null, + "tagLine": "Software Engineer at Google", + "profilePicture": "https://sessionize.com/image/3fde-400o400o1-D3Mpe62o9gxTLBYCzNUcmH.jpg", + "sessions": [ + { + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" }, { - "title": "Company Website", - "url": "https://www.deliveroo.co.uk", - "linkType": "Company_Website" + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764400, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764404, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "96e519b2-b0e1-4232-a71c-21bf3145cb13", - "firstName": "Jason", - "lastName": "Pearson", - "fullName": "Jason Pearson", - "bio": "Jason Pearson is a polyglot full stack engineer, but literally found love via Android at Hinge. He built the app from scratch and currently is the tech lead for Mobile Platform.", - "tagLine": "Senior Staff Android Engineer @ Hinge", - "profilePicture": "https://sessionize.com/image/0912-400o400o2-S5Mb5RFTy9R2bpExpTB4J.jpg", + "id": "18ce6325-d3f4-4478-99d0-5683c4b924c2", + "firstName": "Jimmy", + "lastName": "Ray", + "fullName": "Jimmy Ray", + "bio": "Jimmy, a Principal Software Engineer at Auto Trader, started developing software around 20 years ago and has led teams to build Android apps for over a decade. He is passionate about frameworks, engines, and APIs, clean code and software craftsmanship, driving personal development and growth and having fun doing it. \r\n\r\nJimmy began speaking in 2016 after a long time of attending events and networking. He's presented to various audiences, from meet-ups to large conference talks. Whatever the talk, expect it to be relaxed, humorous, and informative.", + "tagLine": "Principal Android Developer at Auto Trader UK", + "profilePicture": "https://sessionize.com/image/5a01-400o400o1-MouqB96CzuM222BcwB8Akq.jpg", "sessions": [ { - "id": 530286, - "name": "From Laptop Builds to Advanced CI" + "id": 708032, + "name": "DIY Server-Driven UI: Building Auto Trader's SDUI platform" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/kaeawc", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/jasondpearson/", + "url": "https://www.linkedin.com/in/jimmyrayuk/", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://www.jasonpearson.dev/", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://hinge.co/", + "url": "https://www.autotrader.co.uk", "linkType": "Company_Website" } ], @@ -1553,19 +1790,19 @@ "firstName": "John", "lastName": "Rodriguez", "fullName": "John Rodriguez", - "bio": "John is an Android engineer on Cash App where he helps build banking features. Previously, he worked on the Square Point-of-Sale and Developer SDK teams. He is the lead maintainer for Paparazzi and has contributed to LeakCanary, Picasso and Wire.", - "tagLine": "Android @ Square", - "profilePicture": "https://sessionize.com/image/c957-400o400o2-Qguf3YC74ywpVgGWVkEbMm.png", + "bio": "John is an Android engineer @ Duolingo, who previously worked on Cash App and Square's Point-of-Sale and Developer SDK teams. He is the lead maintainer for Paparazzi and has contributed to LeakCanary, Picasso and Wire.", + "tagLine": "Android and Developer Productivity", + "profilePicture": "https://sessionize.com/image/852a-400o400o1-Qguf3YC74ywpVgGWVkEbMm.png", "sessions": [ { - "id": 535307, - "name": "What's new in Paparazzi?" + "id": 736513, + "name": "A Snapshot Preview of Paparazzi 2.0" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/jrodbx", "linkType": "Twitter" }, @@ -1576,7 +1813,7 @@ }, { "title": "Company Website", - "url": "https://cash.app", + "url": "https://duolingo.com/", "linkType": "Company_Website" } ], @@ -1584,35 +1821,25 @@ "categories": [] }, { - "id": "1849dced-2e7b-4d3a-b8f9-3c6e9a62df29", - "firstName": "Jordan", - "lastName": "Terry", - "fullName": "Jordan Terry", - "bio": "Senior Android Developer working on the platform team Marks & Spencer. \r\n\r\nI have a lot of experience gained from making a lot of mistakes. I have years of experience working at Clearscore, the Guardian, Twitter and Marks & Spencer.", - "tagLine": "Marks & Spencer, Senior Android Developer", - "profilePicture": "https://sessionize.com/image/b442-400o400o2-3MGZDUch3vyQrauYfeU6ig.jpg", + "id": "555e52b7-fcb6-49ea-81c7-d9b52cc4c7e9", + "firstName": "Jonathan", + "lastName": "Wendorf", + "fullName": "Jonathan Wendorf", + "bio": "Jonathan is a Partner Engineer at Meta, supporting partners in building spatial apps for Meta Horizon OS.", + "tagLine": "Reality Labs Partner Engineer, Meta", + "profilePicture": "https://sessionize.com/image/b83a-400o400o1-BcZ3Hf3xoNCANekaHM6Fji.png", "sessions": [ { - "id": 501854, - "name": "Developer Productivity On a Budget" + "id": 760426, + "name": "Enter the Metaverse: Android Apps on Meta Horizon OS" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/JordNullable", - "linkType": "Twitter" - }, - { - "title": "Blog", - "url": "https://jordanterry.co.uk", - "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "http://marksandspencer.com", - "linkType": "Company_Website" + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/jkwendorf/", + "linkType": "LinkedIn" } ], "questionAnswers": [], @@ -1625,17 +1852,17 @@ "fullName": "Jose Alcérreca", "bio": "Jose is a Developer Relations Engineer working on Android, trying to make Architecture and Testing effortless for developers.", "tagLine": "Android Developer Relations Engineer at Google", - "profilePicture": "https://sessionize.com/image/b754-400o400o2-35a426a6-e9e7-4da4-8b70-4ecfc7a1211e.jpg", + "profilePicture": "https://sessionize.com/image/a272-400o400o1-nmCkbJM5cwpA536gxVbzp2.jpg", "sessions": [ { - "id": 538660, - "name": "Easy screenshot testing with Compose" + "id": 747072, + "name": "Scalable Testing Strategies" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/ppvi", "linkType": "Twitter" }, @@ -1654,52 +1881,134 @@ "categories": [] }, { - "id": "3851a67a-7332-4247-a6cb-4e87efbf256c", - "firstName": "Josef", - "lastName": "Raska", - "fullName": "Josef Raska", - "bio": "Josef is a Mobile Engineer at Glovo Barcelona and he spent the last decade of his engineering time on Android and related services. Next to apps development he focuses on mobile development infrastructure. His passion is to help mobile projects and engineers grow, reaching higher maturity in the mobile industry. Code is his way of speaking and when he doesn't talk or read some, you can find him traveling, hiking, drinking beer or doing all together.", - "tagLine": "Glovo, Mobile Engineer", - "profilePicture": "https://sessionize.com/image/f00e-400o400o2-b54c788a-427d-434d-a321-ea767f33ef95.jpg", + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "firstName": "Jossi", + "lastName": "Wolf", + "fullName": "Jossi Wolf", + "bio": "Jossi is a Software Engineer working on Jetpack Compose.", + "tagLine": "Working on Compose @Google.", + "profilePicture": "https://sessionize.com/image/5133-400o400o1-fovpVfBDWPMUpKnm33APCJ.png", + "sessions": [ + { + "id": 764343, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764345, + "name": "Ask Android! - Compose, GenAI, Platform" + }, + { + "id": 764406, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764408, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764409, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "85f45c57-98a0-45e9-8a5f-ed6096e268b9", + "firstName": "Kate", + "lastName": "Moksina", + "fullName": "Kate Moksina", + "bio": "Kate has been the sole Android engineer on the Growth team for Monzo Business for the past two years. The team's primary focus is on driving product-led growth through rapid experimentation, while ensuring the high quality people expect of Monzo. Over this period, Kate has contributed to the development of a referral program, the introduction of business savings interest rates, and improvements to the signup process.\r\n\r\nBefore joining Monzo, Kate has worked as a software engineer across various industries, including retail and airlines.", + "tagLine": "Android Engineer for Monzo Business", + "profilePicture": "https://sessionize.com/image/5379-400o400o1-Pqr6usMGqM1fFSfU7gQM9k.jpg", "sessions": [ { - "id": 520431, - "name": "Mobile Observability at scale" + "id": 735932, + "name": "Don't Build to Last: Strategies for Designing and Productionising Experiments" } ], "isTopSpeaker": false, - "links": [ + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "firstName": "Kateryna", + "lastName": "Semenova", + "fullName": "Kateryna Semenova", + "bio": null, + "tagLine": "Sr. Developer Relations Engineer at Google", + "profilePicture": "https://sessionize.com/image/5517-400o400o1-hZzhLvRyPDGmKBX17yG2Q3.jpg", + "sessions": [ { - "title": "Twitter", - "url": "https://twitter.com/josef_raska", - "linkType": "Twitter" + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/josefraska/", - "linkType": "LinkedIn" + "id": 764343, + "name": "Ask Android! - Compose, GenAI, WearOS" }, { - "title": "Blog", - "url": "https://medium.com/@josef.raska", - "linkType": "Blog" + "id": 764345, + "name": "Ask Android! - Compose, GenAI, Platform" + }, + { + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" + }, + { + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764378, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", - "firstName": "Jossi", - "lastName": "Wolf", - "fullName": "Jossi Wolf", - "bio": "Jossi is a Software Engineer working on Jetpack Compose.", - "tagLine": "Working on Compose @Google.", - "profilePicture": "https://sessionize.com/image/2194-400o400o2-Mpe5QRP8bZhUUtdn4WPWkx.jpeg", + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "firstName": "Kevin", + "lastName": "Galligan", + "fullName": "Kevin Galligan", + "bio": "Technology Partner at Touchlab. Kotlin Foundation Ecosystem Committee member. Kotlin GDE, open source contributor, and conference speaker. Helping define the future of Kotlin Multiplatform, and technology platforms in general.", + "tagLine": "Touchlab Technology Partner", + "profilePicture": "https://sessionize.com/image/f487-400o400o1-P6rRPbjNN4Avc7ddBwBGkh.jpg", "sessions": [ { - "id": 530106, - "name": "Building Compose APIs" + "id": 759067, + "name": "The Future of Android, Kotlin, and Everything" + }, + { + "id": 760569, + "name": "Roundtable: KMP & CMP - Yes we can!...but...." } ], "isTopSpeaker": false, @@ -1708,104 +2017,167 @@ "categories": [] }, { - "id": "6284f70b-bae3-4b49-a672-52b5b7ca5124", - "firstName": "Josteve", - "lastName": "Adekanbi", - "fullName": "Josteve Adekanbi", - "bio": "Josteve Adekanbi is a Software Engineer with a keen interest in Machine Learning and expertise in advanced technologies, notably Flutter and Firebase. He has collaborated with leading tech entities, including Codility, and has been instrumental in developing significant projects such as the Klasha app. Alongside his work, Josteve occasionally lends his expertise to the tech community by answering questions on StackOverflow. As a testament to his commitment to continuous learning and expanding his horizons in AI, he is currently pursuing a master's degree in the UK.", - "tagLine": "Software Engineer", - "profilePicture": "https://sessionize.com/image/f725-400o400o2-NyziT6w8LzSrmELhpXorJa.png", + "id": "892ec2dc-5b2e-45fb-9393-ff1407ebebe7", + "firstName": "Konstantin", + "lastName": "Zolotov", + "fullName": "Konstantin Zolotov", + "bio": "Got into Android by rooting, patching and flashing custom firmwares somewhen around Android Gingerbread.\r\nWorked in product teams since 2014 and joined a core team at Bumble in 2022 to make other developers happy.", + "tagLine": "Senior Android Engineer @ Bumble", + "profilePicture": "https://sessionize.com/image/9262-400o400o1-aYVP29WmKPu1Et9gzwGRYL.jpg", "sessions": [ { - "id": 530144, - "name": "Deep Dive into Flutter Animations: Crafting Dynamic and Engaging UIs" + "id": 722874, + "name": "From realtime CameraX filters to General Purpose GPU computing in simple steps" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/jostevee", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/josteve-a-5b3190189/", + "url": "https://www.linkedin.com/in/konstantin-zolotov-563b3968", "linkType": "LinkedIn" + }, + { + "title": "Company Website", + "url": "https://bumble.com", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "462e6b97-01f9-44b1-864c-9d85bedf911b", - "firstName": "Julien", - "lastName": "Salvi", - "fullName": "Julien Salvi", - "bio": "Into the Android world for more than 10 years, I experienced California the startup way of life before coming back to France. I am currently Lead Android Engineer at Aircall where we are building the best phone system for modern businesses. Alongside Android, I have a great interest in backend development with Kotlin and AR/VR technology. In 2021, I became an Android GDE to continue my journey to always share and learn with the Android community.\r\nIn my spare time, I am a world traveler and a beer lover always looking for the best IPAs!", - "tagLine": "Lead Android Engineer @ Aircall", - "profilePicture": "https://sessionize.com/image/dd30-400o400o2-4NcyaNMS4awH1Zr87u5Gve.png", + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "firstName": "Kristina", + "lastName": "Simakova", + "fullName": "Kristina Simakova", + "bio": "Kristina is an Engineering Manager at Google. She is managing Android Media Editing team. The team is working on Media3 Editing", + "tagLine": "Software Engineering Manager @ Google | Android Media Editing", + "profilePicture": "https://sessionize.com/image/a502-400o400o1-nHrPafgjqkDURHAam5xcVD.jpg", "sessions": [ { - "id": 495565, - "name": "Our ongoing journey from REST to GraphQL on Android" + "id": 734739, + "name": "Video Editing on Android with Media3" + }, + { + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764378, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + }, + { + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" + }, + { + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/JulienSalvi", + "title": "X (Twitter)", + "url": "https://x.com/KristiSimakova", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/salvijulien/", + "url": "https://www.linkedin.com/in/ksimakova/", "linkType": "LinkedIn" - }, - { - "title": "Company Website", - "url": "https://aircall.io/", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "d9cf6863-3524-4461-b5d2-ee91cc1595f2", - "firstName": "Kendi", - "lastName": "J", - "fullName": "Kendi J", - "bio": "I am Flutter Dev, who's still open to learning other technologies.\r\n\r\nI love tech communities and I am a co-organizer at Android254, Flutter Kenya and Flutter Africa, which are among the large tech communities within Africa.\r\n\r\nI co-organize Droidcon KE, which is the largest mobile developer conference in Sub-Saharan Africa, aimed at bringing opportunities to mobile app developers in my region.\r\n\r\nI am a technical speaker as well as a technical blog writer. \r\n\r\nBesides tech, I enjoy acting in live theatre and mountains make me really happy :-)", - "tagLine": "Flutter Developer", - "profilePicture": "https://sessionize.com/image/b51e-400o400o2-9ooipa6JYq295VR2jKQ398.jpg", + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "firstName": "Kseniia", + "lastName": "Shumelchyk", + "fullName": "Kseniia Shumelchyk", + "bio": "Kseniia is a Developer Relations Engineer on the Android team at Google, helping developers to design and build great apps for Wear OS.\r\n\r\nPrior to joining Google, Kseniia worked as a full-time Android engineer and was a Google Developer Expert for Android.", + "tagLine": "Engineering Manager @ Google, Android Developer Relations", + "profilePicture": "https://sessionize.com/image/70b6-400o400o1-DXh7tbhXiMNCb3uHdg6hCa.jpg", "sessions": [ { - "id": 527829, - "name": "Seamless Interoperability: Harnessing JNIGen and FFIGen" + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764343, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764397, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764398, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764399, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764400, + "name": "Ask Android! - Compose, WearOS, Platform" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/kendyjaky", + "title": "X (Twitter)", + "url": "https://x.com/kseniiaS", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/kendij/", + "url": "https://uk.linkedin.com/in/kseniiashumelchyk", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://twitter.com/kendyjaky", + "url": "https://d.android.com/wear", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://twitter.com/kendyjaky", + "url": "https://d.android.com/wear", "linkType": "Company_Website" } ], @@ -1813,39 +2185,75 @@ "categories": [] }, { - "id": "c35f2359-2c5c-4b88-a4bc-024a6c025ce4", - "firstName": "Kinnera Priya", - "lastName": "Putti", - "fullName": "Kinnera Priya Putti", - "bio": "Kinnera (she/her) is an Android Developer at ioki, working on platform features for the white label apps. After an electrical engineering degree, she worked in Tokyo with Android and React Native and was a Lead for WomenWhoCode Tokyo. A little over a year ago, she moved to Frankfurt and joined ioki. She believes sugar comes first and will not ask you to prove otherwise.", - "tagLine": "Android developer at ioki", - "profilePicture": "https://sessionize.com/image/c5fc-400o400o2-hgcsScqgaUkpD8DasH6npt.jpg", + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "firstName": "Marcello", + "lastName": "Galhardo", + "fullName": "Marcello Galhardo", + "bio": "Marcello is a seasoned software engineer with over 10 years of experience across web, backend, desktop, and Android development. Having worked in diverse teams at Quandoo, N26, and Delivery Hero, he has contributed to apps with millions of international users.\r\n\r\nCurrently, he works at the AndroidX Architecture Components Team at Google in London, having previously contributed to the SystemUI Team. ", + "tagLine": "Software Engineer", + "profilePicture": "https://sessionize.com/image/e8e9-400o400o1-fsmB2WdNXHRxntPonKJRhn.jpg", "sessions": [ { - "id": 530221, - "name": "Structuring modules in an Android app project" + "id": 764303, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764328, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764329, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764330, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" + }, + { + "id": 696374, + "name": "AndroidX Lifecycle's Path to Multiplatform" + }, + { + "id": 764404, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764406, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764408, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764409, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/kinnerapriyap", + "title": "X (Twitter)", + "url": "https://twitter.com/marcellogalhard", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/kinnerapriyap/", + "url": "https://linkedin.com/in/marcellogalhardo", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://www.kinnerapriyap.com/work/", + "url": "https://marcellogalhardo.dev/", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://ioki.com/", + "url": "https://marcellogalhardo.dev/", "linkType": "Company_Website" } ], @@ -1853,64 +2261,69 @@ "categories": [] }, { - "id": "2313d8eb-7d6e-444d-8e8d-4d223f896ce8", - "firstName": "Maia", - "lastName": "Grotepass", - "fullName": "Maia Grotepass", - "bio": "Mobile dev since Cupcake. Android GDE. Kotlin enthusiast. Dogs make me happy.", - "tagLine": "Staff Engineer: Android principal at Luno", - "profilePicture": "https://sessionize.com/image/1d96-400o400o2-KpWLxBbuVa4M4Pg59vMrZj.png", + "id": "ef7c1115-d852-44b9-9f72-530ba7223c55", + "firstName": "Marcin", + "lastName": "Moskala", + "fullName": "Marcin Moskala", + "bio": "Marcin Moskala is an experienced developer and Kotlin trainer. He is the founder of the Kt. Academy, an official JetBrains partner for Kotlin training, author of the books Effective Kotlin, Kotlin Coroutines, and Android Development with Kotlin. He is also the main author of influential articles about Kotlin and a speaker invited to many programming conferences.", + "tagLine": "Developer during the day, author at night, trainer at Kt. Academy", + "profilePicture": "https://sessionize.com/image/af65-400o400o1-3FjkVzve3a6N6W5Qdawkwk.jpg", "sessions": [ { - "id": 496334, - "name": "90s Website … in 2023 on mobile in Compose … for science" + "id": 700387, + "name": "Mistakes you make using Kotlin Coroutines" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/maiatoday", + "title": "X (Twitter)", + "url": "https://twitter.com/marcinmoskala", "linkType": "Twitter" + }, + { + "title": "Blog", + "url": "https://blog.kotlin-academy.com/", + "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://kt.academy/", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "b4cf78c9-623b-4b8f-b676-097fb7802668", - "firstName": "Manuel", - "lastName": "Vivo", - "fullName": "Manuel Vivo", - "bio": "Manuel is a Staff Android Engineer at Bumble Inc. With previous experience at Capital One and Google, he currently focuses on App Architecture, Dependency Injection and Jetpack Compose.", - "tagLine": "Staff Android Engineer", - "profilePicture": "https://sessionize.com/image/b01e-400o400o2-Kp2Y8HutkygeHUVS5nXh2t.jpg", + "id": "eeb25beb-5d37-4fb7-acac-170357a1d89b", + "firstName": "Marharyta", + "lastName": "Nedzelska", + "fullName": "Marharyta Nedzelska", + "bio": "Margarita is a big Kotlin fan and Kotlin GDE. Knows both conference sides: speaking and organizing. Organized a KUG in her native city Kyiv because she believes in Knowledge Sharing and Collective Intelligence. For her everyday job, she's building Static Code Analysis tools for Java/Kotlin/Scala and other languages.", + "tagLine": "Staff Software Engineer @ Sonar", + "profilePicture": "https://sessionize.com/image/0b1a-400o400o1-e2ba4bc2-bed9-4217-ab0b-2a5b2eb895b1.jpg", "sessions": [ { - "id": 524652, - "name": "Peeling Back the Layers: Unmasking the UI-nknown!" + "id": 704275, + "name": "The state of code coverage for Kotlin" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/manuelvicnt", + "title": "X (Twitter)", + "url": "https://twitter.com/jMargaritaN", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/manuel-vicente-vivo-54498653", + "url": "https://www.linkedin.com/in/marharyta-nedzelska-521806bb", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://manuelvivo.dev/", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://manuelvivo.dev", + "url": "https://www.sonarsource.com/", "linkType": "Company_Website" } ], @@ -1918,69 +2331,78 @@ "categories": [] }, { - "id": "8e7913b6-fce4-419a-b862-7a1b535d1af9", - "firstName": "Manuela Sakura", - "lastName": "Rommel", - "fullName": "Manuela Sakura Rommel", - "bio": "Manuela is a Flutter Developer, with 4 years of experience in the field. She is passionate about using technology to make a positive impact in people's lives, and is particularly focused on accessibility in her work. In addition to her professional experience, Manuela is also a co-organizer of the Berlin Flutter meetup group, where she actively shares her knowledge and learns from others in the community.", - "tagLine": "Flutter Developer", - "profilePicture": "https://sessionize.com/image/5ca3-400o400o2-3YDZFHTMCPzSkZaogNvESN.jpg", + "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", + "firstName": "Maria", + "lastName": "Neumayer", + "fullName": "Maria Neumayer", + "bio": "Maria Neumayer is a Principal Software Engineer currently working at Skyscanner. She’s been working as an engineer since 2010, with a focus on Android. She's an Austrian living in London, previously worked at various small to medium sized companies including Deliveroo, Citymapper and Path.", + "tagLine": "Principal Software Engineer at Skyscanner", + "profilePicture": "https://sessionize.com/image/d345-400o400o1-3wPpJqnGuqemU7jynSggGb.jpg", "sessions": [ { - "id": 517620, - "name": "Stop Treating Accessibility as an Afterthought: Concrete Steps to Build Inclusive Apps" + "id": 757537, + "name": "Roundtable: Mastering Release Management" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/ManuSakuraRo", + "url": "http://twitter.com/marianeum", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/manuelasakurarommel/", + "url": "https://www.linkedin.com/in/marianeumayer/", "linkType": "LinkedIn" + }, + { + "title": "Blog", + "url": "https://medium.com/@marianeum", + "linkType": "Blog" } ], "questionAnswers": [], "categories": [] }, { - "id": "377a3c7a-eba5-48e0-9057-0a5ff91061cc", - "firstName": "Marc", - "lastName": "Obrador Sureda", - "fullName": "Marc Obrador Sureda", - "bio": "I am a passionate of mobile app development and IT security, and I've been lucky enough to combine both of them in my daily job for the past 10 years. A few years ago I co-founded Build38, where we develop tools that enable mobile app developers to build their dream apps in a secure manner.", - "tagLine": "CTO & Co-founder @ Build38", - "profilePicture": "https://sessionize.com/image/acec-400o400o2-TWsGF5LW3CSc9ZnWQt1p9p.jpg", + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "firstName": "Márton", + "lastName": "Braun", + "fullName": "Márton Braun", + "bio": "Márton is a Kotlin Developer Advocate at JetBrains, working on making Kotlin Multiplatform awesome. Passionate about education, he's a guest lecturer at the Budapest University of Technology and Economics.\r\n\r\nHe’s been contributing to the community through writing, speaking, and open source libraries since his university days. He is a Google Developer Expert for Android and Kotlin, and a co-organizer of Android Budapest and Android Worldwide.", + "tagLine": "Developer Advocate at JetBrains", + "profilePicture": "https://sessionize.com/image/e76e-400o400o1-48Xecw4YZ8eXv2jhaMBiqP.png", "sessions": [ { - "id": 540429, - "name": "REST in Peace: A Journey Through API Protection" + "id": 729194, + "name": "What’s New in Compose Multiplatform - A Live Tour" + }, + { + "id": 768515, + "name": "Kotlin by JetBrains, present and future" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/marcobrador", + "title": "X (Twitter)", + "url": "https://twitter.com/zsmb13", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/marc-obrador/", + "url": "https://www.linkedin.com/in/zsmb13/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://medium.com/@marcobrador89", + "url": "https://zsmb.co/", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://build38.com/", + "url": "https://www.jetbrains.com/", "linkType": "Company_Website" } ], @@ -1988,69 +2410,92 @@ "categories": [] }, { - "id": "15d3a177-f21f-4b7b-8f42-3f7694477f21", - "firstName": "Marcin", - "lastName": "Chudy", - "fullName": "Marcin Chudy", - "bio": "Marcin is a Senior Flutter Developer at LeanCode, a software house from Poland known for building complex mobile apps with Flutter and engaging in the Flutter Warsaw community. He is currently playing a Flutter Tech Lead role in a large project for the banking sector. Previously worked with backend, and web frontend (React), finally settling on mobile and falling in love with Flutter at first sight. After work, he enjoys dancing salsa and bachata and attends metal concerts.", - "tagLine": "Senior Flutter Developer at LeanCode", - "profilePicture": "https://sessionize.com/image/a655-400o400o2-SNAUPk2vPnfyvo7yefSjuW.jpeg", + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "firstName": "Matthew", + "lastName": "McCullough", + "fullName": "Matthew McCullough", + "bio": "Helping create great Android developer experiences", + "tagLine": "VP of Product, Android Developer - Google", + "profilePicture": "https://sessionize.com/image/abdf-400o400o1-9YAG1qVuMZspDaunBSV1bg.png", "sessions": [ { - "id": 518711, - "name": "Hitting a Gold Mine: How to Set the Gold Standard With Golden Tests" + "id": 771426, + "name": "#TheAndroidShow - Leadership Keynote" + }, + { + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" } ], "isTopSpeaker": false, - "links": [ + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "firstName": "Matvei", + "lastName": "Malkov", + "fullName": "Matvei Malkov", + "bio": null, + "tagLine": "Software Engineer – Google", + "profilePicture": "https://sessionize.com/image/e4fa-400o400o1-hXE89Nj2H8qTyZustibY9v.jpg", + "sessions": [ { - "title": "Twitter", - "url": "https://twitter.com/mchudy_", - "linkType": "Twitter" + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/chudymarcin", - "linkType": "LinkedIn" + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" }, { - "title": "Company Website", - "url": "https://leancode.co", - "linkType": "Company_Website" + "id": 764406, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764408, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764409, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", - "firstName": "Maria", - "lastName": "Neumayer", - "fullName": "Maria Neumayer", - "bio": "Maria Neumayer is a Principal Software Engineer currently working at Skyscanner. She’s been working as an engineer since 2010, with a focus on Android. She's an Austrian living in London, previously worked at various small to medium sized companies including Deliveroo, Citymapper and Path.", - "tagLine": "Principal Software Engineer at Skyscanner", - "profilePicture": "https://sessionize.com/image/0ab3-400o400o2-3smUHqNxEdFzXZrZCMnHra.jpg", + "id": "084a1449-47c9-488c-b5b6-5b8534c45a23", + "firstName": "Mauro", + "lastName": "Frezza", + "fullName": "Mauro Frezza", + "bio": "10+ years of experience in Android\r\nFocusing on reliability best practices and DDD development.", + "tagLine": "Senior Software Engineer at Hubspot", + "profilePicture": "https://sessionize.com/image/7af0-400o400o1-3cj5xLSu6y4FwLVq418mG8.jpg", "sessions": [ { - "id": 530160, - "name": "From Complex to Seamless - Succeeding in codebase migrations" + "id": 733921, + "name": "Effective App Monitoring in Production" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "http://twitter.com/marianeum", + "title": "X (Twitter)", + "url": "https://twitter.com/maurofrezza", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/marianeumayer/", + "url": "https://de.linkedin.com/in/maurofrezzadeveloper", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://medium.com/@marianeum", + "url": "https://medium.com/@maurofrezza", "linkType": "Blog" } ], @@ -2058,74 +2503,68 @@ "categories": [] }, { - "id": "fc80cf34-563e-4cd4-a04d-23d1191a7812", - "firstName": "Mario", - "lastName": "Bodemann", - "fullName": "Mario Bodemann", - "bio": "Hey,\r\n\r\nI love building things: Developer Relations @ Telekom, Communities @ GDG Berlin Android or strange things with all sorts of materials.\r\n\r\nI'm happt to chat about technology, languages, communities, speaking at conferences and meetups, my cats and everything else. :)", - "tagLine": "Developer Evangelist", - "profilePicture": "https://sessionize.com/image/50b4-400o400o2-XyC5PewUba89TxydMuAtZT.jpg", + "id": "3b95e79a-0e6b-4fcd-b44e-023fa3dfd7cd", + "firstName": "Mayowa", + "lastName": "Egbewunmi", + "fullName": "Mayowa Egbewunmi", + "bio": "Mayowa Egbewunmi is an Android Engineer with over 8 years of development experience; cutting across the Fintech, Telecom, Education and Data sourcing industry. I currently work with Premise Data as a Staff Android Engineer, where my everyday experience involves leading and developing new product initiatives for data collection. With over 4.5millions data contributors using our app in over 40 languages, I am privileged to have led the development of some of our complex data capture initiatives like video capture, audio capture and location constrained capture. Over the last 4 years with Premise Data, I have tackled tough engineering challenges not limited to Architecture, Localization, Security, Hardware Compatibility, Data Synchronization, A/B Testing, App Tracing/Analytics, CI/CD and App Monitoring.\r\n\r\nMy passion is in developing life-changing engineering products using cutting edge technologies with growth and scalability at the core of its engineering. \r\n\r\nIn my spare time, I enjoy watching Football and YouTube.", + "tagLine": "Premise Data, Staff Android Engineer.", + "profilePicture": "https://sessionize.com/image/d83c-400o400o1-sy3mGsMfRa9vH5F1Wan6m6.jpg", "sessions": [ { - "id": 527245, - "name": "Multiplatform USB: How to Communicate Seamlessly" + "id": 731808, + "name": "Streamlining Permission Request in Jetpack Compose" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "http://twitter.com/mariobodemann", + "title": "X (Twitter)", + "url": "https://twitter.com/talk2mayor30", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/mariobodemann/", + "url": "https://www.linkedin.com/in/mayowa-egbewunmi-6b045474/", "linkType": "LinkedIn" }, { - "title": "Instagram", - "url": "https://www.instagram.com/dachgeschoss_katzen/", - "linkType": "Instagram" - }, - { - "title": "Company Website", - "url": "https://developer.telekom.com", - "linkType": "Company_Website" + "title": "Blog", + "url": "https://techmayor.xyz", + "linkType": "Blog" } ], "questionAnswers": [], "categories": [] }, { - "id": "60004028-029a-4865-88eb-acd45debf033", - "firstName": "Mark", - "lastName": "Brown", - "fullName": "Mark Brown", - "bio": "Mark Brown, a Solutions Architect Specialist for MongoDB, brings 30+ years of technical job experience to bear in meeting customer’s challenges. Before joining MongoDB, he was most recently a Software Development Team Lead building medical applications and analytics capabilities on a wide range of mobile and cloud platforms. A native of Central New York, he has happily called Raleigh, North Carolina home since 1999. He enjoys running, skiing, and solving the NYTimes crossword.", - "tagLine": "Solutions Architect Specialist", - "profilePicture": "https://sessionize.com/image/dd4a-400o400o2-4YVJA3Div612MUrYHVfGZk.jpg", + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "firstName": "Meghan", + "lastName": "Stronach", + "fullName": "Meghan Stronach", + "bio": "Meghan is a PM on Qualcomm AI Hub. Born and raised in the Toronto area, she graduated from the University of Waterloo in Management Engineering and has spent her time at companies of various sizes, from big (Apple, Qualcomm) to small startups. She enjoys working with teams of various sizes, ensuring the most effective decision is made, and talking with customers to learn their roadblocks of adopting ML on device. ", + "tagLine": "Qualcomm AI Hub Program Manager", + "profilePicture": "https://sessionize.com/image/54ae-400o400o1-DaRfy7tQ6D8f2gf6fJifUq.png", "sessions": [ { - "id": 528793, - "name": "Sync Data with your Mobile Kotlin App The Easy Way!" + "id": 748950, + "name": "How to Optimize, Validate and Deploy ML Models On Device" + }, + { + "id": 768482, + "name": "How to Optimize, Validate and Deploy ML Models On Device (Part II)" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/MarkBrownNC", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/markbrownnc/", + "url": "https://www.linkedin.com/in/mestrona/", "linkType": "LinkedIn" }, { "title": "Company Website", - "url": "https://mongodb.com", + "url": "https://aihub.qualcomm.com", "linkType": "Company_Website" } ], @@ -2133,124 +2572,135 @@ "categories": [] }, { - "id": "71373c0f-53e8-4020-8d16-1bdfe603c3c7", - "firstName": "Markus", - "lastName": "Hintersteiner", - "fullName": "Markus Hintersteiner", - "bio": "Making Android apps crash less", - "tagLine": "Mobile @ sentry.io", - "profilePicture": "https://sessionize.com/image/26ec-400o400o2-UyW86VPQZCfNmhNaM55sBX.jpg", + "id": "327f56b4-c631-4489-9590-37bdfc856e3a", + "firstName": "Miguel", + "lastName": "Montemayor", + "fullName": "Miguel Montemayor", + "bio": "Developer Relations Engineer at Google working on tablets, foldables, and stylus.", + "tagLine": "Google, Developer Relations Engineer", + "profilePicture": "https://sessionize.com/image/a27c-400o400o1-Up8GrNeGUFEuC8mvsENRDw.jpg", "sessions": [ { - "id": 529442, - "name": "Advanced Android Canvas APIs" + "id": 764350, + "name": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/markushi_", + "title": "X (Twitter)", + "url": "https://x.com/migmontemayor", "linkType": "Twitter" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "firstName": "Miguel", + "lastName": "Montemayor", + "fullName": "Miguel Montemayor", + "bio": null, + "tagLine": "Developer Relations Engineer at Google", + "profilePicture": "https://sessionize.com/image/83b0-400o400o1-XWKwCHiFfrpX391G6vgSyR.jpg", + "sessions": [ + { + "id": 764347, + "name": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors" }, { - "title": "Blog", - "url": "https://markushintersteiner.at/", - "linkType": "Blog" + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" }, { - "title": "Company Website", - "url": "https://sentry.io", - "linkType": "Company_Website" + "id": 764378, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", - "firstName": "Márton", - "lastName": "Braun", - "fullName": "Márton Braun", - "bio": "Developer Advocate at JetBrains. A fan of Kotlin since the 1.0 of the language. Lecturer at BME-VIK, teaching a standalone Kotlin course. Creator of a few open source libraries. Ranked right around the very top of the Kotlin tag on StackOverflow.\r\n", - "tagLine": "Long-time Kotlin enthusiast", - "profilePicture": "https://sessionize.com/image/821b-400o400o2-1c-96d7-480f-9eed-bf5dce444441.544836fc-7bdb-4611-a140-2bda3e9f9655.png", + "id": "d7e7d278-a645-42c5-b337-c793a5b5bd79", + "firstName": "Mikhail", + "lastName": "Dudarev", + "fullName": "Mikhail Dudarev", + "bio": "Mikhail Dudarev is the CTO and co-founder of Licel. He has more than 25 years experience in building and scaling cutting edge tech solutions. He is the creator of one of the first internet banking solutions that was used by 200 banks in total. \r\nAfter starting out as a student working on aeronautical control systems, he moved into the banking industry to design cryptographic hardware and software used for mobile payments, eventually becoming an architect of mobile banking solutions.\r\nMikhail’s expertise in Java, cryptography, and smart card technologies provided the foundations for Licel’s first product, jCardSim, and later the creation of both Stringer Java Obfuscator and DexProtector.", + "tagLine": "CTO of Licel", + "profilePicture": "https://sessionize.com/image/871c-400o400o1-9eZJxjNKgUP1asBXqfMHuG.png", "sessions": [ { - "id": 520549, - "name": "Composing an API the *right* way" + "id": 735816, + "name": "Developing Trusted Applications for Trusted Execution Environments and vTEEs" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/zsmb13", + "title": "X (Twitter)", + "url": "https://x.com/MikhailDudarev", "linkType": "Twitter" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/zsmb13/", - "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://zsmb.co/", - "linkType": "Blog" + "title": "Company Website", + "url": "https://licelus.com", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "24ce44ea-d436-4476-88ac-08695de0acfa", - "firstName": "Matthias", - "lastName": "Geisler", - "fullName": "Matthias Geisler", - "bio": "True believer in (Kotlin) Multiplatform and working with it for over 3 years now. Currently works for LichtBlick SE, to build solutions on Android. Maintainer and developer of KMock.\r\nCo-Organizer of KUG Berlin ", - "tagLine": "Software Engineer@LichtBlick SE", - "profilePicture": "https://sessionize.com/image/79c1-400o400o2-EFHw9K5hdfKBkGe6xBcruK.jpg", + "id": "24eb82e3-3ea7-4b45-9ffb-d63cac580e88", + "firstName": "Mohamed", + "lastName": "Kerroumi", + "fullName": "Mohamed Kerroumi", + "bio": "Mohamed is a technical security expert at Zimperium, within the Customer Success team he advise and support customers across EMEA region for their Mobile Application Protection Suite, he has over 18 years of experience around mobile security, Trusted Execution Environment (TEE) and embedded security and helped many large customers secure their applications such as: banking, mobile payments, Identity, messaging and more.\r\n", + "tagLine": "Zimperium - Solutions Architect", + "profilePicture": "https://sessionize.com/image/f507-400o400o1-9A4bijb6d2LSzVdsnyREM7.jpg", "sessions": [ { - "id": 517414, - "name": "And Gradle says: sharing is caring - Or why Gradle Plugins are all you need for your Configuration" + "id": 745796, + "name": "Securing Android: Tackling Advanced Threats and Enhancing App Security" } ], "isTopSpeaker": false, - "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/BitPogo", - "linkType": "Twitter" - } - ], + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "3e36c7fe-b7ed-4a1a-b197-5937e0c611f6", - "firstName": "Michael", - "lastName": "Tweed", - "fullName": "Michael Tweed", - "bio": "Michael has been working on Android apps for over 10 years. He started his career at Sky, before spending time at a startup and then joining Skyscanner in 2020. He currently leads their Mobile Platform team, building frameworks and tooling for mobile engineers across the company.", - "tagLine": "Principal Software Engineer at Skyscanner", - "profilePicture": "https://sessionize.com/image/7fa6-400o400o2-fuoy8A4yyuRjUXRf5rewZG.jpeg", + "id": "eddfc3bb-2258-45f8-b4a6-50233932a2d2", + "firstName": "Mohamed ", + "lastName": "Kerroumi", + "fullName": "Mohamed Kerroumi", + "bio": "Mohamed Kerroumi is a seasoned mobile app security expert with extensive experience in the tech industry. He has held pivotal roles such as Senior Solutions Architect, Field Applications Engineer across Europe and North America, and R&D Senior Security Engineer at leading companies like Zimperium, Thales, and Intertrust. His expertise spans mobile security solutions, application architecture, and advanced security engineering, making him a key player in the development and implementation of secure mobile technologies globally.", + "tagLine": "Mobile App Security Expert", + "profilePicture": "https://sessionize.com/image/9108-400o400o1-S97pzRz99jVgxSD27aFzur.png", "sessions": [ { - "id": 529504, - "name": "Supporting your architecture with code coverage and static analysis rules" + "id": 735282, + "name": "Securing the Unseen: Advanced Threats, Best Practices & Challenges in Mobile App Security" } ], "isTopSpeaker": false, "links": [ { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/mtweed/", + "url": "https://www.linkedin.com/in/krishna-vishnubhotla", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://www.zimperium.com/blog/mobile-banking-malware-regulation-stress-adaptive-security/", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://www.skyscanner.net/", + "url": "https://www.zimperium.com", "linkType": "Company_Website" } ], @@ -2258,39 +2708,34 @@ "categories": [] }, { - "id": "e6858863-4e16-44ab-b2e0-22c940533654", - "firstName": "Moritz", - "lastName": "Theis", - "fullName": "Moritz Theis", - "bio": "Flutter Developer since 2019, \r\nCoffee Enthusiast since 2016", - "tagLine": "CEO at Snapp X", - "profilePicture": "https://sessionize.com/image/1060-400o400o2-9Rz1NKCMugzgKBLWFd5t2b.jpg", + "id": "a79f3682-df5c-40e4-8aa8-1b6eaa7bdeec", + "firstName": "Mohit", + "lastName": "Sarveiya", + "fullName": "Mohit Sarveiya", + "bio": "Mohit Sarveiya is a Google Developer Expert in Kotlin and Android. Prior to building Android apps, he was a Backend Engineer. He was an early adopter of Kotlin and absolutely loves the language. He has given many talks and workshops on Kotlin.", + "tagLine": "Google Developers Expert Kotlin & Android", + "profilePicture": "https://sessionize.com/image/27b5-400o400o1-KVK4kvFitPjesss8TcUTR3.png", "sessions": [ { - "id": 530253, - "name": "Google Home, But Better: Smart Home Display with Flutter" + "id": 696208, + "name": "Deep Dive into the Compose Compiler" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/MoritzTheis/", + "title": "X (Twitter)", + "url": "https://twitter.com/heyitsmohit", "linkType": "Twitter" }, - { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/moritz-theis-43366b109/", - "linkType": "LinkedIn" - }, { "title": "Blog", - "url": "https://medium.com/snapp-x", + "url": "https://codingwithmohit.com/", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://snappx.io", + "url": "https://codingwithmohit.com/", "linkType": "Company_Website" } ], @@ -2304,17 +2749,17 @@ "fullName": "Neal Michie", "bio": "Neal Michie is Director of Product Management at PACE Anti-Piracy. He believes security should be a top concern for all applications, advocating that it is not just a defensive measure but key to building long term engagement and growth. Neal brings more than two decades of mobile development experience of which more than half has been dedicated to building highly secure mobile solutions, including the first to be fully certified by both Mastercard and Visa.", "tagLine": "PACE Anti-Piracy, Director of Product Management", - "profilePicture": "https://sessionize.com/image/9a3a-400o400o2-pFZomRTw4MxznaYSxjZU6U.jpg", + "profilePicture": "https://sessionize.com/image/27a2-400o400o1-pFZomRTw4MxznaYSxjZU6U.jpg", "sessions": [ { - "id": 528347, - "name": "Become a hero to your DevSecOps team" + "id": 742198, + "name": "Demystifying Code Signing" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/nealmichie", "linkType": "Twitter" }, @@ -2344,11 +2789,11 @@ "fullName": "Neil Hutchison", "bio": "Neil has been working away in London building Android apps for almost a decade, currently working at Toyota Connected on their companion apps. Previously he hosted Londroid and GDG Devfest LDN, among other adhoc events. Always an advocate for the Android community in London and globally.", "tagLine": "Senior Android Engineer @ Toyota Connected Europe", - "profilePicture": "https://sessionize.com/image/91c5-400o400o2-B1bPYZNdFGHYLyEtCjicKM.png", + "profilePicture": "https://sessionize.com/image/e432-400o400o1-89ca7382-3ee3-4fac-ab0c-7d373c1a3c9d.jpg", "sessions": [ { - "id": 522982, - "name": "Staying Relevant" + "id": 757520, + "name": "Roundtable: Keeping Up With Android – How to Stay Relevant" } ], "isTopSpeaker": false, @@ -2363,42 +2808,45 @@ "categories": [] }, { - "id": "89694c9d-5de0-4f39-a50a-ed48d6eb56ca", - "firstName": "Nelson", - "lastName": "Osacky", - "fullName": "Nelson Osacky", - "bio": "Former Square in San Francisco. Former SoundCloud in Berlin. Now speeding up builds around the world at Gradle.", - "tagLine": "Lead Solutions Engineer at Gradle", - "profilePicture": "https://sessionize.com/image/c346-400o400o2-LwWQapfqBG4M3xK56t7wUK.jpg", + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "firstName": "Nick", + "lastName": "Butcher", + "fullName": "Nick Butcher", + "bio": null, + "tagLine": "Jetpack Compose Product Manager", + "profilePicture": "https://sessionize.com/image/2424-400o400o1-BWig3xpwFS3x4825k89RxN.jpg", "sessions": [ { - "id": 528351, - "name": "Becoming and Staying a Productive Developer with Build Scans, Build Validation Scripts and Gradle" - } - ], - "isTopSpeaker": false, - "links": [ + "id": 764303, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, { - "title": "Twitter", - "url": "https://twitter.com/nellyspageli", - "linkType": "Twitter" + "id": 764328, + "name": "Ask Android! - Compose, KMP, Gemini in AS" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/nosacky", - "linkType": "LinkedIn" + "id": 764329, + "name": "Ask Android! - Compose, KMP, Gemini in AS" }, { - "title": "Blog", - "url": "https://osacky.com", - "linkType": "Blog" + "id": 764330, + "name": "Ask Android! - Compose, KMP, Gemini in AS" }, { - "title": "Company Website", - "url": "https://gradle.com", - "linkType": "Company_Website" + "id": 764397, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764398, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764399, + "name": "Ask Android! - Compose, WearOS, Platform" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, @@ -2409,17 +2857,17 @@ "fullName": "Nicola Corti", "bio": "Nicola Corti is a Google Developer Expert for Kotlin. He has been working with the language since before version 1.0, and he is the maintainer of several open-source libraries and tools for mobile developers.\r\n\r\nHe's currently working in the React Native Core team at Meta, building one of the most popular cross-platform mobile framework. \r\n\r\nFurthermore, he is an active member of the developer community. \r\nHis involvement goes from speaking at international conferences to being a member of CFP committees and supporting developer communities across Europe.\r\n\r\nIn his free time, he also loves baking, podcasting, and running.", "tagLine": "Kotlin GDE - Android @ React", - "profilePicture": "https://sessionize.com/image/c795-400o400o2-0b-3fb7-450d-ab71-08c9dd81a48a.14d46ff0-032b-4678-8d21-f712d4a97457.jpeg", + "profilePicture": "https://sessionize.com/image/2c35-400o400o1-0b-3fb7-450d-ab71-08c9dd81a48a.14d46ff0-032b-4678-8d21-f712d4a97457.jpeg", "sessions": [ { - "id": 529946, - "name": "Brick by Brick: Building Open Source libraries" + "id": 757517, + "name": "Roundtable: Community-Driven Development: How to Contribute to OSS" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/cortinico", "linkType": "Twitter" }, @@ -2447,19 +2895,19 @@ "firstName": "Nicole", "lastName": "Terc", "fullName": "Nicole Terc", - "bio": "Boardgame lover, videogame addict and origami enthusiast, Nicole self taught herself to code and has been fooling around with the Android ecosystem for more than 10 years. She has participated in a diverse variety of projects around the world and has recently been recognized as an Android Google Expert. Regardless of what the current adventures have taken her, she loves finding time to play with new things and share her experience :) ", + "bio": "Android GDE, Boardgame lover, videogame addict and origami enthusiast, Nicole self taught herself to code and has been fooling around with the Android ecosystem for more than 10 years. She has participated in a diverse variety of projects for several clients around the world, including video streaming, news, social media and public transport applications. Regardless of what the current adventures have taken her, she loves finding time to play with new things and share her experience :)", "tagLine": "Android Tech Lead @ HubSpot", - "profilePicture": "https://sessionize.com/image/350d-400o400o2-G9xcJMvyiQXy5uEyUthvFx.jpg", + "profilePicture": "https://sessionize.com/image/ac6b-400o400o1-G9xcJMvyiQXy5uEyUthvFx.jpg", "sessions": [ { - "id": 527210, - "name": "Bluetooth Magic, first level" + "id": 728763, + "name": "Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/nicole_terc", "linkType": "Twitter" }, @@ -2472,35 +2920,10 @@ "title": "Blog", "url": "https://github.com/nicole-terc", "linkType": "Blog" - } - ], - "questionAnswers": [], - "categories": [] - }, - { - "id": "c746f9bf-2963-4549-b2f8-26cdafa24936", - "firstName": "Oliver", - "lastName": "Williams", - "fullName": "Oliver Williams", - "bio": "Mobile Application Security Lead for Zimperium within EMEA. Background in mobile, network and API security.", - "tagLine": "EMEA Mobile Application Security Lead, Zimperium", - "profilePicture": "https://sessionize.com/image/a373-400o400o2-nHhgwjFckj7GH6TsHyqT7M.jpg", - "sessions": [ - { - "id": 527630, - "name": "White-box Cryptography" - } - ], - "isTopSpeaker": false, - "links": [ - { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/oliverjameswilliams/", - "linkType": "LinkedIn" }, { "title": "Company Website", - "url": "https://www.zimperium.com/mobile-app-protection/", + "url": "https://www.hubspot.com/", "linkType": "Company_Website" } ], @@ -2512,19 +2935,19 @@ "firstName": "Paolo", "lastName": "Rotolo", "fullName": "Paolo Rotolo", - "bio": "I've been fascinated by computers and software since I was young: at 12 I discovered the open-source world, joined the Ubuntu Community and started my journey in the software development magic. \r\nAndroid developer since 2013 when I bought my first Android phone. I immediately fell in love with the platform and started developing some projects like Numix and libraries like AppIntro and then bringing that passion to work. Now I'm developing Indoor Positioning and Navigation systems at Nextome in Conversano (Puglia), while I finish my Master's Degree in Computer Engineering at Politecnico di Bari.", - "tagLine": "Android Engineer @ Nextome", - "profilePicture": "https://sessionize.com/image/c801-400o400o2-WC1GQd6RazodCBypFhrBzj.png", + "bio": "I've been fascinated by computers and software since I was young: at 12 I discovered the open-source world, joined the Ubuntu Community and started my journey in the software development magic. \r\nAndroid developer since 2013 when I bought my first Android phone. I immediately fell in love with the platform and started developing some projects like Numix and libraries like AppIntro and then bringing that passion to work.\r\nCurrently working at Blinkist in Berlin.", + "tagLine": "Android Developer @ Blinkist", + "profilePicture": "https://sessionize.com/image/f7ab-400o400o1-QzhppnJNfomm63LvD9aVkr.jpg", "sessions": [ { - "id": 529946, - "name": "Brick by Brick: Building Open Source libraries" + "id": 734441, + "name": "A journey in Android's BLE world" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/paolorotolo", "linkType": "Twitter" }, @@ -2545,7 +2968,7 @@ }, { "title": "Company Website", - "url": "https://www.nextome.com/", + "url": "http://blinkist.com/", "linkType": "Company_Website" } ], @@ -2553,39 +2976,149 @@ "categories": [] }, { - "id": "7a816772-cc23-42e7-818e-4fd5153f7283", - "firstName": "Parth", - "lastName": "Padgaonkar", - "fullName": "Parth Padgaonkar", - "bio": "Parth has been developing Android apps since 2013! He currently serves the mobile team at Mercury, working on creating accessible and beautiful financial services for small businesses and startups. He received a B.A. in Linguistics & Cognitive Science from Claremont McKenna College and occasionally puts it to use when iterating on UI. He's worked on 8 different Android apps and SDKs - here's to many more! Keywords: Brazilian Jiu-Jitsu, Android dev, diversity advocate, swing dancer, feminist", - "tagLine": "Mobile Eng Manager @ Mercury", - "profilePicture": "https://sessionize.com/image/5a4e-400o400o2-KBLec5PTo4uj7SnrsXkPZw.jpg", + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "firstName": "Paul", + "lastName": "Lammertsma", + "fullName": "Paul Lammertsma", + "bio": "Paul supports developers with Android integrations at Google as a Developer Relations Engineer and has been passionate about building amazing Android experiences since its early days.", + "tagLine": "Developer Relations Engineer, Android", + "profilePicture": "https://sessionize.com/image/27fa-400o400o1-NGyC1Cgp5F1nK2XuxBbGFB.jpg", "sessions": [ { - "id": 510034, - "name": "Hot Take: Engineering Managers are actually useful!" + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/JvmName", + "title": "X (Twitter)", + "url": "https://twitter.com/officesunshine", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/parthpadg/", + "url": "https://www.linkedin.com/in/paullammertsma/", + "linkType": "LinkedIn" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "695e56a6-62a8-496b-9082-8cc4dadec337", + "firstName": "Petras", + "lastName": "Vičiūnas", + "fullName": "Petras Vičiūnas", + "bio": "Focused on making engineers lives easier by improving developer workflows and tools. While I’m relatively new to the Android space, I’m excited to learn more at Droidcon.", + "tagLine": "Uber, Software Engineer II", + "profilePicture": "https://sessionize.com/image/eb2f-400o400o1-QbQY9HNbyFQ4H1Z6rnP7LE.jpg", + "sessions": [ + { + "id": 736447, + "name": "You don't have to run it locally! How to run your emulators in the cloud." + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/petras-viciunas/", + "linkType": "LinkedIn" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "f5e37102-a8be-4e34-be47-b98329cd2a23", + "firstName": "Phellipe", + "lastName": "Silva", + "fullName": "Phellipe Silva", + "bio": "My name is Phellipe, I am a senior Android engineer with more than 10 years of experience building Android apps. I am currently based in London and working @Wise, a fintech with millions of active users. Our mission is to make international money transfers cheap, fair, and simple. Among my favourite topics I can mention automated testing, career progression and server driven UI.", + "tagLine": "Senior Android Engineer @Wise", + "profilePicture": "https://sessionize.com/image/46f3-400o400o1-JzupkvSpziyA27WGbVmUFk.jpg", + "sessions": [ + { + "id": 709487, + "name": "Dynamic Flow, the Wise approach to server driven UI" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/phellipeafsilva", "linkType": "LinkedIn" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "firstName": "Ran", + "lastName": "Nachmany", + "fullName": "Ran Nachmany", + "bio": null, + "tagLine": "Developer Relations Engineer at Google", + "profilePicture": "https://sessionize.com/image/b66b-400o400o1-S1DfV4mNMFp7Xe43xsriNP.jpg", + "sessions": [ + { + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" }, { - "title": "Blog", - "url": "https://jvmname.dev", - "linkType": "Blog" + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "firstName": "Ran ", + "lastName": "Nachmany", + "fullName": "Ran Nachmany", + "bio": "Ran compiled his first code when he was 6 and has been moving bits since.", + "tagLine": "Developer Relation Engineer, Android. ", + "profilePicture": "https://sessionize.com/image/dd07-400o400o1-3b3d7366-75f0-462a-9c47-26621a990848.jpg", + "sessions": [ + { + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764378, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "Twitter", + "url": "https://twitter.com/shed2k", + "linkType": "Twitter" + }, + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/rannachmany/", + "linkType": "LinkedIn" }, { "title": "Company Website", - "url": "https://mercury.com", + "url": "http://www.google.com", "linkType": "Company_Website" } ], @@ -2593,144 +3126,204 @@ "categories": [] }, { - "id": "f117aa78-7e11-451d-b7ba-fb19ac866f31", - "firstName": "Pascal", - "lastName": "Welsch", - "fullName": "Pascal Welsch", - "bio": "Pascal (Flutter GDE) had a 6-year-long Android background before he transitioned to ditch Kotlin for Dart and Flutter. At PHNTM he leads a team of Flutter talents delivering stunning user interfaces to audiences on all platforms.\r\nPascal is a strong believer in testing and open-source. If you find any of his packages in your dependencies they are certainly well tested. Like his Feedback SDK Wiredash.", - "tagLine": "Flutter GDE, CTO at PHNTM, Co-Founder of Wiredash", - "profilePicture": "https://sessionize.com/image/e24b-400o400o2-78wbjoZ3Xgx7wcpS3couEB.png", + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "firstName": "Rebecca", + "lastName": "Franks", + "fullName": "Rebecca Franks", + "bio": "Developer Relations Engineer at Google on the Jetpack Compose team. ", + "tagLine": "Developer Relations Engineer at Google", + "profilePicture": "https://sessionize.com/image/dce8-400o400o1-Hiy1N2ZrkGQSLJ557LRgzz.png", "sessions": [ { - "id": 517499, - "name": "Exploring Records and Patterns" + "id": 764303, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764399, + "name": "Ask Android! - Compose, WearOS, Platform" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/passsy", + "url": "https://twitter.com/riggaroo", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/pascal-welsch-679a2b80/", + "url": "https://www.linkedin.com/in/rebeccafranks/", "linkType": "LinkedIn" }, { - "title": "Company Website", - "url": "https://pascal.page", - "linkType": "Company_Website" + "title": "Blog", + "url": "https://riggaroo.dev", + "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://developer.android.com", + "linkType": "Company_Website" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "firstName": "Rebecca", + "lastName": "Franks", + "fullName": "Rebecca Franks", + "bio": null, + "tagLine": "Senior Android Developer Relations Engineer", + "profilePicture": "https://sessionize.com/image/3eab-400o400o1-JaamjSb59JumfXLBFqRe2Z.jpg", + "sessions": [ + { + "id": 764329, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764330, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" + }, + { + "id": 764331, + "name": "Ask Android! - Compose, GenAI, WearOS" + }, + { + "id": 764391, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764396, + "name": "Ask Android! - Compose, Media, Platform" + }, + { + "id": 764397, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764398, + "name": "Ask Android! - Compose, WearOS, Platform" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "e5941ea5-5a10-45a0-903b-f642db802a16", - "firstName": "Pietro", - "lastName": "Maggi", - "fullName": "Pietro Maggi", - "bio": "I believe that the best way to understand how something works is to take it apart (and possibly put it back together at the end). My children share this belief, which results in a lot of broken toys in our house!\r\nI like spending time with my family and climbing mountains. ", - "tagLine": "Developer Advocate", - "profilePicture": "https://sessionize.com/image/a5ed-400o400o2-a5-5a10-45a0-903b-f642db802a16.086c6cf4-1bf3-4ae5-9de6-216485181daf.JPG", + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "firstName": "Rebecca", + "lastName": "Gutteridge", + "fullName": "Rebecca Gutteridge", + "bio": "Rebecca is a Staff Developer Relations Engineer at Google, leading the Android System UI platform team and Compose Adaptive team. She spends her time in the intersection of engineering and design; and works on helping developers create beautiful UIs and creating high quality apps. \r\n", + "tagLine": "Developer Relations Engineer", + "profilePicture": "https://sessionize.com/image/9824-400o400o1-MRWirm23XHsu3vRvBGV9Hc.jpg", "sessions": [ { - "id": 528020, - "name": "Trust no one - Introducing the Play Integrity API" + "id": 764328, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" } ], "isTopSpeaker": false, "links": [ { "title": "Twitter", - "url": "https://twitter.com/pfmaggi", + "url": "https://twitter.com/_BexSG_", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/pietromaggi/", + "url": "https://www.linkedin.com/in/rebecca-gutteridge", "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://medium.com/@pmaggi", - "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://www.android.com/enterprise/", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "b6dc4b56-ca5d-4e55-8a57-e37f6e6d6af0", - "firstName": "Piotr", - "lastName": "Prus", - "fullName": "Piotr Prus", - "bio": "I am an android developer with 5+ years of experience, addicted to new things. I love clean architecture and clean, simple designs. I am trying to create meaningful articles and speeches. Currently composing and KMMing all the things.", - "tagLine": "Android developer", - "profilePicture": "https://sessionize.com/image/7cba-400o400o2-ainA1gVSSCHxugeFJdVjM2.jpg", + "id": "4674e36f-50f8-4ffb-a486-679d730c759e", + "firstName": "Renaud", + "lastName": "Mathieu", + "fullName": "Renaud Mathieu", + "bio": "Organizer GDG Paris Android.", + "tagLine": "Freelance Android", + "profilePicture": "https://sessionize.com/image/f3b9-400o400o1-2952bcff-8886-47d8-911d-b70321f9b768.jpg", "sessions": [ { - "id": 513549, - "name": "Blast Off: Managing Hundreds of UI Updates for Emoji Cannons" + "id": 720892, + "name": "Seamless mobile real-time communication with WebRTC" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/piotr_prus", + "title": "X (Twitter)", + "url": "https://twitter.com/renaud_mathieu", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/piotrprus/", + "url": "https://www.linkedin.com/in/renaudmathieu1/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://piotrprus.medium.com/", + "url": "https://renaudmathieu.com", "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://renaudmathieu.com", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "a13934a7-6137-4a94-9a03-84919fce12f4", - "firstName": "Rafa", - "lastName": "Moreno", - "fullName": "Rafa Moreno", - "bio": "Currently A Software Engineer at Clubhouse working on the Android app", - "tagLine": "Software Engineer at Clubhouse", - "profilePicture": "https://sessionize.com/image/2a36-400o400o2-UCB8qwzbXEfzyYpzFyqHT5.png", + "id": "da380fb8-c8ba-47e9-b224-26647296cff5", + "firstName": "Richard", + "lastName": "Das", + "fullName": "Richard Das", + "bio": "With over 20 years experience in software engineering, product design & management, Richard has worked with some of the world’s most recognized brands to transform their business, build products customers love, and empower teams to do their best work. As Product Lead at Apple’s Enterprise Design Lab, he guided teams to create innovative, user-focused products. At Deloitte he helped scale the Mobile team and deliver solutions across public and private sector. Now at Ditto, he helps customers scale innovative, real-time solutions that drive their businesses.", + "tagLine": "Leading teams to build Apps customers love", + "profilePicture": "https://sessionize.com/image/ea4b-400o400o1-XJgZjbq6YPAdcfDhCfGU59.jpg", "sessions": [ { - "id": 495999, - "name": "A Picture Is Worth A 1,000 ... Lines? Devs?: Get the full picture (on image loading) with Coil" + "id": 757989, + "name": "Rethinking Apps with Local-First Architecture" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/cemore2048", + "title": "X (Twitter)", + "url": "https://x.com/richarddas", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://linkedin.com/in/rafaelmorenocesar", + "url": "https://linkedin.com/in/richarddas", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://richarddas.com", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://clubhouse.com", + "url": "https://ditto.live", "linkType": "Company_Website" } ], @@ -2738,78 +3331,70 @@ "categories": [] }, { - "id": "cae62422-4040-4130-b345-29c8cd65f41b", - "firstName": "Raouf", - "lastName": "Rahiche", - "fullName": "Raouf Rahiche", - "bio": "I'm a passionate Flutter and Desktop App developer with a passion for crafting exceptional apps that delight users. As a Google Developer Expert in this field, I've had the privilege of speaking at public events in multiple countries and sharing my knowledge through various mediums, including YouTube tutorials and in-depth articles. Contributing to the open-source community is important to me, and I'm always willing to lend a hand on Stack Overflow or offer guidance to fellow developers.", - "tagLine": "GDE For Flutter and Dart", - "profilePicture": "https://sessionize.com/image/d6f7-400o400o2-Hb6tM3nxHB2PQ3wwmMgynT.jpg", + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "firstName": "Rob", + "lastName": "Orgiu", + "fullName": "Rob Orgiu", + "bio": "Roberto è uno sviluppatore Android che viaggia molto. Gli piace scattare fotografie e visitare posti nuovi.", + "tagLine": "Developer Relations Engineer @ Google", + "profilePicture": "https://sessionize.com/image/e3a5-400o400o1-Xng3TgUHQGPahwUV2Am3SX.jpg", "sessions": [ { - "id": 529978, - "name": "Building RTL-Ready Flutter Apps" - } - ], - "isTopSpeaker": false, - "links": [ + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" + }, { - "title": "Twitter", - "url": "https://twitter.com/raoufrahiche", - "linkType": "Twitter" + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/raoufrahiche/", - "linkType": "LinkedIn" + "id": 764372, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" }, { - "title": "Blog", - "url": "https://medium.com/@rahiche", - "linkType": "Blog" + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "firstName": "Rebecca", - "lastName": "Franks", - "fullName": "Rebecca Franks", - "bio": "Developer Relations Engineer at Google on the Jetpack Compose team. ", - "tagLine": "Developer Relations Engineer at Google", - "profilePicture": "https://sessionize.com/image/5a50-400o400o2-UF1e8zFMss7SHNf2dpANeG.png", + "id": "152f613f-841b-407d-9a65-3703ec2dfae2", + "firstName": "Romain", + "lastName": "Guy", + "fullName": "Romain Guy", + "bio": "Romain manages the Android Toolkit team. The team's projects included AndroidX/Jetpack, the UI toolkit, Kotlin support and graphics.", + "tagLine": "Android Toolkit", + "profilePicture": "https://sessionize.com/image/6615-400o400o1-d068ae37-2e0e-49bd-a4be-b376a32367a8.jpg", "sessions": [ { - "id": 538663, - "name": "Fireside Chat with Googlers" - }, - { - "id": 538655, - "name": "Practical Magic with Animations in Compose" + "id": 736883, + "name": "Optimizing Kotlin Code in Practice" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/riggaroo", + "title": "X (Twitter)", + "url": "http://twitter.com/romainguy", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/rebeccafranks/", + "url": "https://www.linkedin.com/in/romainguy/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://riggaroo.dev", + "url": "https://www.curious-creature.com", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://developer.android.com", + "url": "http://www.google.com", "linkType": "Company_Website" } ], @@ -2817,39 +3402,29 @@ "categories": [] }, { - "id": "4674e36f-50f8-4ffb-a486-679d730c759e", - "firstName": "Renaud", - "lastName": "Mathieu", - "fullName": "Renaud Mathieu", - "bio": "Organizer GDG Paris Android.", - "tagLine": "Freelance Android", - "profilePicture": "https://sessionize.com/image/f208-400o400o2-2952bcff-8886-47d8-911d-b70321f9b768.jpg", + "id": "da013a11-7cb0-4b0d-a37e-e29ce98688e0", + "firstName": "Roman", + "lastName": "Zavarnitsyn", + "fullName": "Roman Zavarnitsyn", + "bio": "Builds, infrastructure, CI, SDKs, bytecode", + "tagLine": "Mobile @ sentry.io", + "profilePicture": "https://sessionize.com/image/d0e5-400o400o1-NsU2wXhnTYvi7B4JZV4Wqr.jpg", "sessions": [ { - "id": 502453, - "name": "I have a Date" + "id": 732956, + "name": "Rewind and Resolve: A deep dive into building Session Replay for Android" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/renaud_mathieu", + "title": "X (Twitter)", + "url": "https://x.com/romtsn", "linkType": "Twitter" }, - { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/renaudmathieu1/", - "linkType": "LinkedIn" - }, - { - "title": "Blog", - "url": "https://renaudmathieu.com", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://renaudmathieu.com", + "url": "https://sentry.io/", "linkType": "Company_Website" } ], @@ -2857,29 +3432,24 @@ "categories": [] }, { - "id": "990dea5b-f32e-4260-8551-967e2597bee2", - "firstName": "Renuka", - "lastName": "Kelkar", - "fullName": "Renuka Kelkar", - "bio": "Renuka Kelkar is DevRel Advocate @ Invertase and GDE for Flutter and Dart. Founder of TechPowerGirls Community. Along with this, she is an organizer for GDG London and a Womentechmaker ambassador.", - "tagLine": " Flutter GDE | DevRel @ Invertase.io | Founder@TechPowerGirls ", - "profilePicture": "https://sessionize.com/image/11c0-400o400o2-aghJ3J36oLzzghnwywyfvM.png", + "id": "33801296-8d41-4df0-b729-c0461afa1be2", + "firstName": "Ruslan", + "lastName": "Latypov", + "fullName": "Ruslan Latypov", + "bio": "I am a Software Engineer at Meta, where I have been working for the past 4.5 years on various parts of Mobile Infrastructure. For the past 1.5 years my primary focus has been on migrating Meta's Kotlin infrastructure to K2. ", + "tagLine": "Software Engineer, Meta", + "profilePicture": "https://sessionize.com/image/d7f4-400o400o1-pVapLj4gzVxz1TAj5WNnqv.jpg", "sessions": [ { - "id": 518103, - "name": "Nested Navigation in flutter web" + "id": 730918, + "name": "Upgrading Meta's Kotlin Infrastructure to K2: A Migration Journey" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/KelkarRenuka", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/renukakelkar/", + "url": "https://www.linkedin.com/in/ruslan-latypov-sd/", "linkType": "LinkedIn" } ], @@ -2887,29 +3457,24 @@ "categories": [] }, { - "id": "7d912e80-f02c-42cf-92db-98e670bef1a6", - "firstName": "Roberto", - "lastName": "Orgiu", - "fullName": "Roberto Orgiu", - "bio": "Born in Italy in 1987, passionate for everything that switches on and off and particularly for handheld and wearable devices.\r\nI love to learn new languages such as Kotlin, patterns and everything Android-related.", - "tagLine": "Developer Relations Engineer @ Google", - "profilePicture": "https://sessionize.com/image/0213-400o400o2-80-f02c-42cf-92db-98e670bef1a6.eefe1bb7-b14f-4b68-a09f-510d368ef096.jpg", + "id": "bcf3c75a-e105-41de-8891-53f6b673e58a", + "firstName": "Sachin", + "lastName": "Sapkale", + "fullName": "Sachin Sapkale", + "bio": "I’m a Senior Software Engineer at NewDay Cards Ltd in London, with 11 years of Android development experience across fintech, sports, medical, education, and value-added sectors. Moving to the UK with my family two years ago has been an exciting chapter, deepening my focus on mobile app development and helping me approach projects with more empathy to empower users.\r\nOutside of work, I enjoy cooking and playing table tennis!", + "tagLine": "Senior Software Engineer, NewDay", + "profilePicture": "https://sessionize.com/image/0d5c-400o400o1-9DxDSCGh7AeSsYsAP7sfdN.png", "sessions": [ { - "id": 538652, - "name": "Why is my app letterboxed?!" + "id": 734309, + "name": "Crafting Narratives: Shaping TalkBack with Compose Semantics" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "http://twitter.com/_tiwiz", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "http://www.linkedin.com/in/robertoorgiu", + "url": "https://www.linkedin.com/in/sachin-sapkale-76482070", "linkType": "LinkedIn" } ], @@ -2917,64 +3482,76 @@ "categories": [] }, { - "id": "76117eec-d3f7-49c7-9684-c19058f241a0", - "firstName": "Romain", - "lastName": "Rastel", - "fullName": "Romain Rastel", - "bio": "Romain Rastel is Lead Developer at Dailyn. He started to learn Flutter in the end of 2017, since then he never stopped! \r\n\r\nHe created some open sources packages like flutter_staggered_grid_view, flutter_slidable, and a lot more.\r\nHe also wrote some articles available here: https://romain-rastel.medium.com/", - "tagLine": "Flutter Lead Developer at Dailyn", - "profilePicture": "https://sessionize.com/image/6776-400o400o2-AiSC1723cDbYM1DTMx6KM.jpg", + "id": "d713390d-9628-412f-9778-9c45aeb6b13e", + "firstName": "Satish", + "lastName": "Shende", + "fullName": "Satish Shende", + "bio": "Satish is a part of android dev rel team @ google. Satish advocates and help developers optimized for for Large screens , TV and Auto.", + "tagLine": "Android Engineer - Dev Rel Android", + "profilePicture": "https://sessionize.com/image/bce5-400o400o1-swTpvQe47BUCXbGfT8766W.jpg", "sessions": [ { - "id": 517498, - "name": "How Custom RenderObjects can make your life easier" + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" } ], "isTopSpeaker": false, - "links": [ + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "firstName": "Satish", + "lastName": "Shende", + "fullName": "Satish Shende", + "bio": null, + "tagLine": "Android Engineer - Dev Rel Android", + "profilePicture": "https://sessionize.com/image/bce5-400o400o1-swTpvQe47BUCXbGfT8766W.jpg", + "sessions": [ { - "title": "Twitter", - "url": "https://twitter.com/lets4r", - "linkType": "Twitter" + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" }, { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/romain-rastel/", - "linkType": "LinkedIn" + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" } ], + "isTopSpeaker": false, + "links": [], "questionAnswers": [], "categories": [] }, { - "id": "f1275dc9-492b-4ab1-b31b-3e19bccde628", - "firstName": "Ryan", - "lastName": "Harter", - "fullName": "Ryan Harter", - "bio": "Hailing from the suburbs of Chicago, Ryan has been a part of the Android community since the early days. For a long time, he built creative graphics apps, working everywhere from the top of RecyclerViews all the way down into OpenGL. Since joining Dropbox in 2022, he’s been working on Design Systems, Compose and lots of other parts of the Android stack.", - "tagLine": "Android @ Dropbox", - "profilePicture": "https://sessionize.com/image/82e7-400o400o2-Lb6HdRzoze6vwGkr7VShQ4.jpg", + "id": "8b008502-24e7-4af7-a7a8-d92a7e683271", + "firstName": "Scott", + "lastName": "Alexander-Bown", + "fullName": "Scott Alexander-Bown", + "bio": "Senior Android Engineer and author with deep knowledge of Android and passion for mobile security. Loves trail running, Mexican food and Belgium beer. Founder of SWmobile meetup group based in Bristol/Bath. https://www.meetup.com/swmobile/", + "tagLine": "Senior Andriod Engineer", + "profilePicture": "https://sessionize.com/image/0dad-400o400o1-SLQAA9vfH8h1EhsW4zzsWt.jpg", "sessions": [ { - "id": 529068, - "name": "Demystifying Dagger" + "id": 735950, + "name": "What's new in Android 15 Security?" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/rharter", - "linkType": "Twitter" + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/scottyab", + "linkType": "LinkedIn" }, { "title": "Blog", - "url": "http://ryanharter.com/", + "url": "https://scottyab.com", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://www.dropbox.com/", + "url": "https://scottyab.com", "linkType": "Company_Website" } ], @@ -2982,29 +3559,43 @@ "categories": [] }, { - "id": "6519e3d1-ca80-46b3-99f5-0f77adfdc91f", - "firstName": "Sean", - "lastName": "Higgins", - "fullName": "Sean Higgins", - "bio": "Sean is a Mobile Field Engineer at Instabug, helping developers learn how to better monitor, prioritize and debug performance and stability related issues on mobile. He has spent more than 10 years working in the world of web based and mobile software development and is based in Toronto, Canada.", - "tagLine": "Beyond the Basics: Performance Monitoring and User Experience for Mobile App Growth", - "profilePicture": "https://sessionize.com/image/6d33-400o400o2-ELVtoZ8Bk7gjv8gNr92oyb.jpeg", + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "firstName": "Sebastian", + "lastName": "Aigner", + "fullName": "Sebastian Aigner", + "bio": "As a Kotlin and Compose Multiplatform Developer Advocate at JetBrains, Sebastian spends a lot of time thinking about how technology can advance and inspire people. When he first tried Kotlin, it was love at first sight. He is one of the hosts of the Talking Kotlin podcast, and creates videos for the official Kotlin YouTube channel. Sebastian loves to develop networked applications, uses Kotlin on a variety of platforms, and passionately tinkers on his programs until late into the night.", + "tagLine": "Developer Advocate at JetBrains", + "profilePicture": "https://sessionize.com/image/2ef9-400o400o1-XGxKBoqZvxxQxosrZHQHTT.png", "sessions": [ { - "id": 528097, - "name": "Beyond the Basics: Performance Monitoring and User Experience for Mobile App Growth" + "id": 729194, + "name": "What’s New in Compose Multiplatform - A Live Tour" + }, + { + "id": 768515, + "name": "Kotlin by JetBrains, present and future" } ], "isTopSpeaker": false, "links": [ + { + "title": "X (Twitter)", + "url": "https://twitter.com/sebi_io", + "linkType": "Twitter" + }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/company/instabug/", + "url": "https://www.linkedin.com/in/sebastian-aigner/", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "http://sebi.io", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://www.instabug.com", + "url": "https://www.jetbrains.com/", "linkType": "Company_Website" } ], @@ -3018,17 +3609,37 @@ "fullName": "Sebastiano Poggi", "bio": "Emerging from the foggy plains of northern Italy 🇮🇹 after working at a smartwatch startup, Sebastiano moved with his curls to London 🇬🇧 💂 to do great things at AKQA and then Novoda. After a few years at JetBrains, he's now joined Google and is in Italy once again 🇮🇹. He really wishes he had more time to write technical articles on his blog, but he's live coding on Twitch in the meantime on https://codewiththeitalians.it", "tagLine": "UX Engineer at Google working on Android Developer UX", - "profilePicture": "https://sessionize.com/image/a2c9-400o400o2-BPpvfRRxNbAKepJruufHDH.jpg", + "profilePicture": "https://sessionize.com/image/ff9e-400o400o1-BPpvfRRxNbAKepJruufHDH.jpg", "sessions": [ { - "id": 508933, - "name": "Meet Jewel: create IDE plugins in Compose ✨" + "id": 731273, + "name": "Project Sparkles: how Compose is changing Android Studio" + }, + { + "id": 764303, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764328, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764329, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764330, + "name": "Ask Android! - Compose, KMP, Gemini in AS" + }, + { + "id": 764323, + "name": "Ask Android! - Compose, KMP, Gemini in AS (copy)" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/seebrock3r", "linkType": "Twitter" }, @@ -3052,109 +3663,124 @@ "categories": [] }, { - "id": "d3469e78-7d62-4945-be1f-b9b3cc4a134b", - "firstName": "Shree", - "lastName": "Bhagwat", - "fullName": "Shree Bhagwat", - "bio": "Founder of Codeaamy: An App Development Company. \r\n\r\nFlutter Pune Organiser \r\n\r\nFlutter Tech Editor, Video Course Creator and lead mentor at Raywenderlich.com. \r\n\r\nCorporate Trainer For Flutter\r\n\r\nYoutuber and blogger. \r\n\r\nOn a mission to make education free.", - "tagLine": "Founder / CEO - Codeaamy", - "profilePicture": "https://sessionize.com/image/1616-400o400o2-3sKDKHMpdRYYpdVE3k8S3Z.png", + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "firstName": "Simona", + "lastName": "Milanovic", + "fullName": "Simona Milanovic", + "bio": "Simona Milanovic works at Google as an Android Developer Relations Engineer, as part of the Jetpack Compose team. Before joining Google, Simona worked as an Android Dev in a variety of different industries - retail, transportation, banking, automotive & online dating. Aside from work, she loves to travel, read, organise stuff (😬), play music & video games and spend time with her 4 fluffy pets.", + "tagLine": "Android Developer Relations Engineer @Google", + "profilePicture": "https://sessionize.com/image/ffb6-400o400o1-J2sMoXmDyVkv2JJ6ka71hz.png", "sessions": [ { - "id": 517710, - "name": "Introduction to Game Development with Flutter and Flame" + "id": 751161, + "name": "Designing scalable Compose APIs" + }, + { + "id": 764400, + "name": "Ask Android! - Compose, WearOS, Platform" + }, + { + "id": 764404, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764406, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764408, + "name": "Ask Android! - Compose, Gemini in AS, KMP" + }, + { + "id": 764409, + "name": "Ask Android! - Compose, Gemini in AS, KMP" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/shreebhagwat94", + "title": "X (Twitter)", + "url": "https://twitter.com/anomisSi", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/shrihriday/", + "url": "https://www.linkedin.com/in/anomis/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://medium.com/@shreebhagwat94", + "url": "https://medium.com/@anomisSi", "linkType": "Blog" - }, - { - "title": "Company Website", - "url": "https://www.codeaamy.com", - "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", - "firstName": "Simona", - "lastName": "Milanovic", - "fullName": "Simona Milanovic", - "bio": "Simona Milanovic works at Google, as an Android Developer Relations Engineer for Jetpack Compose. Simona previously worked as an Android Dev in a variety of different industries - retail, transportation, banking, automotive & online dating. Aside from work, she loves to travel, read, organise stuff (😬), play music & video games and spend time with her 4 fluffy pets.", - "tagLine": "Android DevRel Engineer for Jetpack Compose @Google", - "profilePicture": "https://sessionize.com/image/4b8c-400o400o2-J2sMoXmDyVkv2JJ6ka71hz.png", + "id": "9a7f5add-ce7a-49a3-ab3d-f3c4484453e8", + "firstName": "Sinan", + "lastName": "Kozak", + "fullName": "Sinan Kozak", + "bio": "Sinan is an Android GDE and he works as Staff Engineer at Delivery Hero, food delivery company. He has been working with Android since 2011 and has 11 years of professional experience with software development. He is passionate about Kotlin and loves to contribute open-source.", + "tagLine": "Android Staff Engineer - Delivery Hero", + "profilePicture": "https://sessionize.com/image/eb46-400o400o1-SPKGk1Vse2mophBVWYhs2c.jpg", "sessions": [ { - "id": 538649, - "name": "Migrating to Jetpack Compose - an interop love story" + "id": 735538, + "name": "Unblocking The Main Thread: Solving ANRs and Frozen Frames" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/anomisSi", + "title": "X (Twitter)", + "url": "https://twitter.com/snnkzk", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/anomis/", + "url": "https://www.linkedin.com/in/sinankozak/", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://medium.com/@anomisSi", + "url": "https://sinankozak.medium.com/", "linkType": "Blog" + }, + { + "title": "Company Website", + "url": "https://www.deliveryhero.com/", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "85d2aab0-cf59-492c-83fb-ff0a8490fb85", - "firstName": "Stelios", - "lastName": "Frantzeskakis", - "fullName": "Stelios Frantzeskakis", - "bio": "Stelios is a Staff Engineer at Perry Street Software, publisher of the LGBTQ+ dating apps SCRUFF and Jack’d, serving a community of more than 30 million members worldwide.\r\n\r\nHe has been professionally working with Android for more than 10 years, since the release of Android Gingerbread. He's passionate about Clean Architecture, Domain-Driven Design, Test-Driven Development and Behavior-Driven Development.", - "tagLine": "Staff Engineer @ PSS", - "profilePicture": "https://sessionize.com/image/ff42-400o400o2-3zNzHLwqTKYPndJnNfcktN.jpg", + "id": "1caff1f9-9594-4ce8-a031-ed1ba2201d13", + "firstName": "Steven", + "lastName": "Kideckel", + "fullName": "Steven Kideckel", + "bio": "Steven has over 10 years experience working on Android apps, from small startups to Google, and now Patreon. Originally from Canada, he now lives in the New York area with his wife and two cats.", + "tagLine": "Staff Software Engineer at Patreon", + "profilePicture": "https://sessionize.com/image/ab0f-400o400o1-KKUzCzjjD9gpyGYY8XFXxn.jpg", "sessions": [ { - "id": 530024, - "name": "Put Your Tests on a Diet: Testing the Behavior and Not the Implementation" + "id": 731650, + "name": "Elevated Dependency Injection: Going Beyond the Basics with Custom Hilt Components" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/SteliosFran", - "linkType": "Twitter" - }, - { - "title": "Blog", - "url": "https://steliosf.com/posts/", - "linkType": "Blog" + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/steven-kideckel-0448a346/", + "linkType": "LinkedIn" }, { "title": "Company Website", - "url": "https://perrystreet.com/", + "url": "https://www.patreon.com/", "linkType": "Company_Website" } ], @@ -3168,17 +3794,17 @@ "fullName": "Tamás Bazsonyi", "bio": "Tamas Bazsonyi is a member of the Solutions Engineering team at Bitrise working with customers in the EMEA region to implement CI/CD pipelines successfully that help them during their day-to-day jobs. During his career he’s been working as a Full Stack Developer, QA Engineer and also jumping into mobile app development. Some of his favourite topics are Test Automation and Automation in general.\r\n", "tagLine": "Solutions Engineer @ Bitrise", - "profilePicture": "https://sessionize.com/image/cf6f-400o400o2-SkVDLSGbDnPvvdXC2rsYxj.jpg", + "profilePicture": "https://sessionize.com/image/617a-400o400o1-SkVDLSGbDnPvvdXC2rsYxj.jpg", "sessions": [ { - "id": 529968, - "name": "Remote Build Cache - overcoming the network latency tax" + "id": 736362, + "name": "Streamlining Android app development and release processes" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", + "title": "X (Twitter)", "url": "https://twitter.com/bazscsa", "linkType": "Twitter" }, @@ -3202,39 +3828,66 @@ "categories": [] }, { - "id": "ce5f643d-771a-4b14-b504-329c50924cc3", - "firstName": "Tarik", - "lastName": "Eshaq", - "fullName": "Tarik Eshaq", - "bio": "I've worked at Mozilla for over 2 years. At Mozilla, I write software that powers Firefox Sync and Firefox Accounts in Firefox for Android, Firefox for iOS and Firefox Desktop.\r\n\r\nI've spent a long time writing cross-platform Rust that ships to the client on the user's devices but I have also worked on the backend systems that enable authentication and authorization to a user's data.", - "tagLine": "Senior Software Engineer - Mozilla", - "profilePicture": "https://sessionize.com/image/a6cc-400o400o2-DnWc4P81fiotqrgdrGZ33Z.jpg", + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "firstName": "Thomas", + "lastName": "Weathers", + "fullName": "Thomas Weathers", + "bio": "Thomas is a Developer Relations Engineer at Google focusing on Android development for Cars!", + "tagLine": "Developer Relations Engineer, Google", + "profilePicture": "https://sessionize.com/image/acf2-400o400o1-BF9PnBBgFN81pKFk7XFrdo.jpg", + "sessions": [ + { + "id": 764365, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764370, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764381, + "name": "Ask Android! -GenAI, Media, Adaptive apps on all form factors" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "257fcb0c-cc84-4892-a1f1-54985071e0a6", + "firstName": "Timo", + "lastName": "Drick", + "fullName": "Timo Drick", + "bio": "Timo is the Android Lead developer at Seven Principles Mobility GmbH since 2015. He has been working with Android since 2010 and was one of the first adopters of Jetpack Compose.\r\n", + "tagLine": "Lead Android developer at Seven Principles Mobility GmbH", + "profilePicture": "https://sessionize.com/image/1f21-400o400o1-w15eX7WfB1n3dofjoyDoPu.jpg", "sessions": [ { - "id": 519579, - "name": "Shipping production ready Rust for Android" + "id": 734664, + "name": "Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets." } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/tarikeshaq", + "title": "X (Twitter)", + "url": "https://twitter.com/appsonair", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://linkedin.com/in/tarikeshaq", + "url": "https://www.linkedin.com/in/timo-drick-75a10360", "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://tarikeshaq.com", + "url": "https://medium.com/@timo_86166", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://mozilla.org", + "url": "https://7p-mobility.com", "linkType": "Company_Website" } ], @@ -3242,69 +3895,69 @@ "categories": [] }, { - "id": "c95ac59a-7c1e-45b6-ba71-cf6ed861ac4a", - "firstName": "Tomáš", - "lastName": "Mlynarič", - "fullName": "Tomáš Mlynarič", - "bio": "Tomáš is an Android Developer Relations Engineer at Google. His focus is on demystifying Jetpack Compose app performance and simplifying the onboarding process for Baseline Profiles. Before joining Google, Tomáš worked as an Android Engineer with a strong interest in dependency injection and modularization.", - "tagLine": "Android Developer Relations Engineer @Google", - "profilePicture": "https://sessionize.com/image/0b88-400o400o2-TYBw6tNjGjraeENLmgC7oE.jpg", + "id": "e841b549-a549-420e-ba73-3039c567a609", + "firstName": "Tin", + "lastName": "Novakovic", + "fullName": "Tin Novakovic", + "bio": "I'm Tin, a self taught Android Engineer currently at Compare The Market, I'm a lover of Compose, Coroutines and multi-platform technologies.\r\n\r\nMy career wouldn't have started without an open tech community and that's why I'm here, to give back to a community that has given me so much.", + "tagLine": "Compare The Market, Android Engineer", + "profilePicture": "https://sessionize.com/image/13ea-400o400o1-EVEhEbzLsMfx3Tx7Hrb3cp.png", "sessions": [ { - "id": 538657, - "name": "Enhancing Jetpack Compose App Performance: Tools, Strategies and Optimizations" + "id": 733188, + "name": "Improving App Performance With Custom Thread Pools" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/mlykotom", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/mlykotom/", + "url": "https://www.linkedin.com/in/tinnova/", "linkType": "LinkedIn" }, { - "title": "Blog", - "url": "https://medium.com/@mlykotom", - "linkType": "Blog" + "title": "Company Website", + "url": "https://www.comparethemarket.com/", + "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] }, { - "id": "31f0ce3c-630f-4146-a6f5-9e6c7f5cad62", - "firstName": "Tyler", - "lastName": "Shukert", - "fullName": "Tyler Shukert", - "bio": "DevRel and Flutter SDK maintainer at Supabase. Tyler is constantly creating content around Flutter and Supabase to help the community build in a weekend, scale to millions. ", - "tagLine": "DevRel @Supabase", - "profilePicture": "https://sessionize.com/image/ec10-400o400o2-EZeDWuWP9C4z9ekP5wjuLu.png", + "id": "1fb7ac5d-d848-4d40-840e-d8ae38128ab4", + "firstName": "Tom", + "lastName": "Colvin", + "fullName": "Tom Colvin", + "bio": "Tom Colvin is CTO of Apptaura, the app development specialists; and founder of Conseal Security, the mobile app security experts. He has been a developer for over 20 years and worked with Android since Cupcake. He is a Google Developer Expert in Android.", + "tagLine": "Android GDE, CTO at Apptaura", + "profilePicture": "https://sessionize.com/image/e21e-400o400o1-UbVs97PLcfGWSoCh1JGpp5.jpg", "sessions": [ { - "id": 530074, - "name": "Bring the power of Postgres to your Flutter app with Supabase" + "id": 736493, + "name": "Asynchrony and the infinite conveyor belt: Advanced coroutines and flows" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/dshukertjr", + "title": "X (Twitter)", + "url": "https://twitter.com/tdcolvin", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/dshukertjr/", + "url": "https://www.linkedin.com/in/tdcolvin/", "linkType": "LinkedIn" }, + { + "title": "Blog", + "url": "https://medium.com/@tdcolvin", + "linkType": "Blog" + }, { "title": "Company Website", - "url": "https://supabase.com/", + "url": "https://www.apptaura.com", "linkType": "Company_Website" } ], @@ -3312,44 +3965,82 @@ "categories": [] }, { - "id": "606a1cb6-edd2-42bd-8bd4-08a51ef41c7f", - "firstName": "Vadym", - "lastName": "Pinchuk", - "fullName": "Vadym Pinchuk", - "bio": "Greetings to the mobile community! I am a passionate mobile Software Engineer with Ukrainian origins, currently residing in London, UK. My journey commenced as an Android Developer in early 2012, and in 2018, I became an active member of the Flutter community. \r\nDuring last two years I worked as an Android Developer at Instagram, but now returned back to the tech stack I love as a Flutter Developer at Labrys.", - "tagLine": "Mobile Software Engineer at Labrys", - "profilePicture": "https://sessionize.com/image/b137-400o400o2-ErZFiwG22BJuohJaCd9ctt.png", + "id": "58038e33-703e-497c-9f47-3624e36418e8", + "firstName": "Tomasz", + "lastName": "Słuszniak", + "fullName": "Tomasz Słuszniak", + "bio": "Tomasz is an experienced Android developer. Brought up on multiple smaller projects of his own, he learned quite a variety of skills. He has taken part in multiple hackathons across Europe, proving his ability to quickly learn and adapt by often scoring top places - and that is how once he was introduced to IoT, for which he has a great enthusiasm since then. Having his startup-like mindset he was a member of Enactus organisation and Google for Startups member at Google Campus in Warsaw.\r\nCurrently in his free time he attempts to join the realms of IoT and Android, as well as tries to contribute to some open source ideas. \r\nOn the other side - big coffee lover, always experimenting with brewing or roasting his own coffee.", + "tagLine": "Android developer by day, IoT enthusiast by night", + "profilePicture": "https://sessionize.com/image/f046-400o400o1-7f8g7J6J2uVj6UyRTd9Hmz.jpg", "sessions": [ { - "id": 512823, - "name": "Latest State of Flutter Feature for Developing Multiple-Entry Android Application" + "id": 735915, + "name": "Android driver - application of Android device in RC vehicle development" } ], "isTopSpeaker": false, "links": [ - { - "title": "Twitter", - "url": "https://twitter.com/vad_pinchuk", - "linkType": "Twitter" - }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/vpinchuk/", + "url": "https://www.linkedin.com/in/tomasz-sluszniak/", "linkType": "LinkedIn" + } + ], + "questionAnswers": [], + "categories": [] + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "firstName": "Tor", + "lastName": "Norbye", + "fullName": "Tor Norbye", + "bio": null, + "tagLine": "Engineering Director at Google", + "profilePicture": "https://sessionize.com/image/a2f5-400o400o1-Uju83YBpinHo2t15pNnkCj.jpg", + "sessions": [ + { + "id": 771426, + "name": "#TheAndroidShow - Leadership Keynote" }, { - "title": "Instagram", - "url": "https://www.instagram.com/vad.pinchuk/", - "linkType": "Instagram" + "id": 771431, + "name": "#TheAndroidShow - Panel Discussion" + } + ], + "isTopSpeaker": false, + "links": [], + "questionAnswers": [], + "categories": [] + }, + { + "id": "edd46ee9-3e55-478c-8f27-c8f86ca77138", + "firstName": "Tushar", + "lastName": "Varshney", + "fullName": "Tushar Varshney", + "bio": "Tushar is working as Android Partner Engineer in Meta working with Google Android Partnerships and Android OS Readiness across Meta's family of apps. Prior to Meta Tushar has been building android apps for last 8 years. ", + "tagLine": "Android Engineer- Meta", + "profilePicture": "https://sessionize.com/image/2402-400o400o1-TXyrFETVGEJfzCvKaSo3Jx.jpg", + "sessions": [ + { + "id": 733770, + "name": "User Initiated Data Transfer Jobs" + } + ], + "isTopSpeaker": false, + "links": [ + { + "title": "LinkedIn", + "url": "https://www.linkedin.com/in/varshneytushar/", + "linkType": "LinkedIn" }, { "title": "Blog", - "url": "https://linktr.ee/vpinchuk", + "url": "https://android-developers.googleblog.com/2024/03/android-14-meta-early-adoption-enhanced-user-experience.html", "linkType": "Blog" }, { "title": "Company Website", - "url": "https://www.labrys.tech/", + "url": "https://www.meta.com/", "linkType": "Company_Website" } ], @@ -3357,39 +4048,34 @@ "categories": [] }, { - "id": "226909f8-7c0a-43bc-b296-a3f2c369b9a3", - "firstName": "Walmyr", - "lastName": "Carvalho", - "fullName": "Walmyr Carvalho", - "bio": "Android Engineer with more than 11 years of experience, Google Developer Expert for Android, mentor at Google for Startups Brazil and creator of Android Dev BR - the biggest Brazilian Android community out there! Currently playing a lot with Bluetooth, smart devices and Camera APIs in general at Polaroid, a camera company based in Amsterdam, The Netherlands! ", - "tagLine": "Lead Android Engineer @ Polaroid, Google Developer Expert for Android", - "profilePicture": "https://sessionize.com/image/4251-400o400o2-HC3qpBAwoLDmcA7E7Skqb9.jpeg", + "id": "34b0d49b-00dd-4dc1-ac1c-6de040e8a5b9", + "firstName": "Vitalii", + "lastName": "Markus", + "fullName": "Vitalii Markus", + "bio": "Hey, I'm Vitalii, an Android Engineer at Flo Health Inc. We're building \"The #1 women's health app worldwide with 60 million monthly active users.\" I'm working in the Health Monitor team, responsible for providing our customers with the most relevant information about their health, symptoms, and patterns.\r\nLiving in Barcelona provides an excellent opportunity for an active lifestyle and many outdoor activities. I usually play tennis and beach volleyball with our IT community; sometimes, we hike in the beautiful places of Catalonia; in our free time, we enjoy local restaurants and craft breweries and discuss new trends in IT.\r\nThe rest of the time, I usually spend time with my family, kid, and 8-kilos cat.", + "tagLine": "Android Engineer @ Flo Health Inc.", + "profilePicture": "https://sessionize.com/image/a8d6-400o400o1-QtWKx2F3qzuiD6f1THAgkk.jpg", "sessions": [ { - "id": 530295, - "name": "Now smile (also in 3D)! Exploring AR, ML and Camera related APIs on Android" + "id": 714221, + "name": "Jetpack Compose: Drawing without pain and recomposition" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/walmyrcarvalho", + "title": "X (Twitter)", + "url": "https://twitter.com/vit_ius", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/walmyrcarvalho/", + "url": "https://www.linkedin.com/in/vitalii-markus/", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://walmyrcarvalho.com.br/", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://polaroid.com", + "url": "https://flo.health/", "linkType": "Company_Website" } ], @@ -3397,24 +4083,24 @@ "categories": [] }, { - "id": "bb2df6d6-edb0-416a-85fc-83b062c6c10c", - "firstName": "Yevhen", - "lastName": "Pekutovskyi", - "fullName": "Yevhen Pekutovskyi", - "bio": "I'm an Android enthusiast with over 8 years of field experience and a background in crafting Android solutions for Unity. \r\nCurrently, I'm a Lead Android Developer at Just Eat Takeaway.com, where I'm passionate about crafting top-notch user experiences. ", - "tagLine": "Lead Android developer at Just Eat Takeaway.com", - "profilePicture": "https://sessionize.com/image/f298-400o400o2-GLsg3NtuFW2mCW5HkAjbUm.jpg", + "id": "0aef86d4-a69b-4a82-bd8b-5ee05a3e421d", + "firstName": "Walid", + "lastName": "Tazout", + "fullName": "Walid Tazout", + "bio": "Embedded Software Engineer with a passion for tech in cars.", + "tagLine": "QA Engineer @ Spotify", + "profilePicture": "https://sessionize.com/image/0b80-400o400o1-aSgVqUyMfcyLmF1nUyLNqr.jpg", "sessions": [ { - "id": 529420, - "name": "Unlocking Unity for Android Developers: Blending Android and Unity in Harmony" + "id": 736123, + "name": "How we build Spotify for Android Automotive" } ], "isTopSpeaker": false, "links": [ { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/eugenepekutovskyi", + "url": "https://www.linkedin.com/in/tazout-walid/", "linkType": "LinkedIn" } ], @@ -3422,68 +4108,71 @@ "categories": [] }, { - "id": "163e9ce7-9042-4482-8f9e-f713d92ceae4", - "firstName": "Yoni", - "lastName": "Tietz", - "fullName": "Yoni Tietz", - "bio": "Software Engineer with over five years of experience in Android Development. Passionate about user interaction with the features we build and producing the best possible work environment for a team to grow.", - "tagLine": "Android Engineer at Trainline", - "profilePicture": "https://sessionize.com/image/43b2-400o400o2-7SxpVudeGQr1ZeJHVP7gYv.jpeg", + "id": "ffd6d0b9-9615-4f5a-9f58-6dcf3d4acb3b", + "firstName": "Wojtek", + "lastName": "Kaliciński", + "fullName": "Wojtek Kaliciński", + "bio": "I'm currently a software engineer on the PubNub SDK team, working on modernizing our JVM SDKs and introducing KMP into our codebase, with a focus on improving both internal and external developer experience. Previously I spent 8 years on the Android team at Google, advocating for and building developer tools and APIs for Android developers.", + "tagLine": "Software Engineer @ PubNub", + "profilePicture": "https://sessionize.com/image/664b-400o400o1-pnyiWWFGB3cNzvGworqTLU.jpg", "sessions": [ { - "id": 530222, - "name": "App architecture - singular vs plural" + "id": 717692, + "name": "Level up your SDKs with KMP - no rewrite required!" } ], "isTopSpeaker": false, "links": [ { - "title": "LinkedIn", - "url": "https://www.linkedin.com/in/yoni-tietz-93b94b173", - "linkType": "LinkedIn" + "title": "X (Twitter)", + "url": "https://twitter.com/wkalic", + "linkType": "Twitter" } ], "questionAnswers": [], "categories": [] }, { - "id": "94bb39bf-8256-4633-adcd-54d621242cea", - "firstName": "Zsolt", - "lastName": "Kocsi", - "fullName": "Zsolt Kocsi", - "bio": "Passionate about people, product, and tech alike. I never cease to be amazed by engineering ingenuity and new paradigms.", - "tagLine": "Principal Android engineer at Bumble", - "profilePicture": "https://sessionize.com/image/e34e-400o400o2-bf-8256-4633-adcd-54d621242cea.f18e5280-6cd4-4ae1-b591-2ba9a6545f61.jpg", + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "firstName": "Xiaodao", + "lastName": "Wu", + "fullName": "Xiaodao Wu", + "bio": "Android Large Screen, Activity Embedding, Android OEM", + "tagLine": "Google, Android Developer Relation Engineer", + "profilePicture": "https://sessionize.com/image/caa3-400o400o1-whudwDLLLXycC5awV2eUG8.jpg", "sessions": [ { - "id": 529128, - "name": "Navigation superpowers at your fingertips" + "id": 764353, + "name": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors" + }, + { + "id": 764351, + "name": "Ask Android! - WearOS, Media, Adaptive apps on all form factors" + }, + { + "id": 764376, + "name": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors" } ], "isTopSpeaker": false, "links": [ { - "title": "Twitter", - "url": "https://twitter.com/ZsoltKocsi", + "title": "X (Twitter)", + "url": "https://x.com/XiaodaoWu", "linkType": "Twitter" }, { "title": "LinkedIn", - "url": "https://www.linkedin.com/in/zsolt-kocsi/", + "url": "https://www.linkedin.com/in/xiaodao-wu/", "linkType": "LinkedIn" }, - { - "title": "Blog", - "url": "https://medium.com/@zsolt.kocsi", - "linkType": "Blog" - }, { "title": "Company Website", - "url": "https://bumble.com/", + "url": "https://www.google.com", "linkType": "Company_Website" } ], "questionAnswers": [], "categories": [] } -] \ No newline at end of file +] diff --git a/shared/src/commonMain/resources/sponsor_sessions.json b/shared/src/commonMain/resources/sponsor_sessions.json index a9378923f..431dcff49 100644 --- a/shared/src/commonMain/resources/sponsor_sessions.json +++ b/shared/src/commonMain/resources/sponsor_sessions.json @@ -5,5840 +5,7228 @@ "sessions": [ { "questionAnswers": [], - "id": "529143", - "title": "Challenges, failures and lessons in 15 years of building on top of Android.", - "description": "15 years in Android development and a whole lot of fun—Carl , founder of Novoda and the guy (and friends) who kickstarted droidcon London, invites you to a whirlwind ride down memory lane. it's a celebration of Android, the community, and the crazy roller coaster of a journey they've been on together.\r\n\r\nGet ready for:\r\n\r\n- A trip back to Android's 'Wild West' days—remember when everyone was figuring it all out?\r\n- A toast to droidcon London, the event that became the go-to hub for Android aficionados.\r\n- Real talk on building Android teams—because, there has been a bit of learning ", - "startsAt": "2023-10-26T09:15:00", - "endsAt": "2023-10-26T10:05:00", + "id": "731273", + "title": "Project Sparkles: how Compose is changing Android Studio", + "description": "We use Android Studio every day, and appreciate how its rich feature set makes our job easier. Most people know that Android Studio is built on the IntelliJ Platform, the same that underpins the popular IntelliJ IDEA from JetBrains, which has seen lasting success for over 20 years. It’s a solid, expansive, and by far the best foundation on which we could stand on to deliver Android-oriented goodies.\r\n\r\nHowever, some parts of the IntelliJ Platform show the signs of time; in particular its UI framework, Swing, is proving the most limiting, having been around for almost 30 years. Don’t get us wrong — it works, and the IDEs themselves prove you can ship complex UIs by using Swing on the IntelliJ Platform. But as we looked at how nice it is to develop UIs on Android by using Jetpack Compose, we thought: why don’t we do the same?\r\n\r\nEnter Project Sparkles, which aims at gradually introducing new high-quality, polished UI surfaces in Android Studio, developed in Compose for Desktop, with all the bells and whistles you can expect from a top-tier interface. In this talk, we’ll cover how Project Sparkles is impacting the development of Android Studio, addressing long-standing user feedback, and how we’re working together with other teams at Google and JetBrains to build a framework to make your favourite IDE even better and easier to understand.\r\n\r\nWe’ll demonstrate a few examples of features already shipping that are powered by Project Sparkles, explain what our goals and ambitions are, and even show some sneak peeks of things you may see in a future Studio version. UI enthusiasts, assemble!", + "startsAt": "2024-10-31T09:10:00", + "endsAt": "2024-10-31T10:00:00", "isServiceSession": false, "isPlenumSession": true, "speakers": [ { - "id": "c74036f4-aebb-4fae-899a-7fb7cb377d05", - "name": "Carl-Gustaf Harroch" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 209173, + "id": 264357, "name": "Keynote" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185719, - "name": "Other" + "id": 264379, + "name": "Android Studio" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530106", - "title": "Building Compose APIs", - "description": "...you won't believe how easy this is! Let's look at challenges you might encounter on your journey of building an API (in Jetpack Compose).\r\n\r\nThrough the example of Jetpack Compose's AnchoredDraggable API, we will build the most basic version of the component and iterate. What functionality do you need? How do you create a concise, easy-to-understand component? What layering do you want to strive for, and how do you incorporate feedback from developers?\r\n\r\nYou will take away a solid understanding of what building an API entails, an example API design process as well as Compose-specific design details and how this maps to non-library codebases.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "736883", + "title": "Optimizing Kotlin Code in Practice", + "description": "In this session we will look at a series of optimizations that were done in Jetpack Compose to learn how different types of optimizations can affect performance. These optimizations include code flow/algorithms, data structures, low-level bytecode optimizations, and memory optimizations. You will learn how to access and understand the bytecode and the machine code that your Kotlin code produces, so you can discover how to improve your applications on your own.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", - "name": "Jossi Wolf" + "id": "152f613f-841b-407d-9a65-3703ec2dfae2", + "name": "Romain Guy" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185724, - "name": "API" + "id": 264359, + "name": "Kotlin" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "522881", - "title": "Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love", - "description": "Kotlin Multiplatform Mobile (KMM) is awesome for us Android Developers. Writing multiplatform code with it doesn't diverge much from our usual routine, and now with Compose Multiplaform, we can write an entire iOS app without knowing a thing about iOS development. But to truly conquer the world of mobile development, we need to bring the iOS developers to our side. Join us in this exciting presentation where we unravel the secrets of crafting Kotlin Multiplatform libraries that your iOS teammates won’t believe they weren’t written in Swift. Discover how Kotlin constructs are translated into Objective-C/Swift and explore effective strategies for addressing any translation problems. Learn tricks that ensure iOS developers will enjoy a frictionless experience while consuming KMM APIs. By using KMM one can significantly reduce development time and bug count.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "731970", + "title": "Wireless protocols for the next generation IoT devices", + "description": "In an era where mobile apps and IoT devices are becoming increasingly interconnected, understanding the nuances of modern wireless protocols is crucial for software developers. This technical talk aims to delve into the latest advancements and practical applications of wireless communication technologies, including Bluetooth Low Energy (BLE), WiFi, Ultra-Wideband (UWB), Thread, LoRaWAN, and more. Learn when and how to use each protocol and their pros and cons. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "2e8bbf8c-544f-460d-8145-78f6693c8ee4", - "name": "André Oriani" + "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", + "name": "Erik Hellman" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264369, + "name": "Other" }, { - "id": 185710, - "name": "KMP" + "id": 264370, + "name": "IoT" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264371, + "name": "Wearables" }, { - "id": 185732, + "id": 264381, "name": "Cross-Platform" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "519579", - "title": "Shipping production ready Rust for Android", - "description": "In this talk, you'll learn why Mozilla invested in Rust for Android and how we've built open-source libraries to make it easier for others to do so as well. You will also learn when not to use Rust and the challenges that still remain with reaching into native code from the JVM.\r\n\r\nThe Firefox Sync team at Mozilla has been shipping production Rust code in Firefox Android for a couple of years now. Rust provides value because it's performant, cross-platform and memory-safe. Delivering production-ready Rust libraries to Android was challenging, but we believe we've made it better!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "735950", + "title": "What's new in Android 15 Security?", + "description": "Audience: Android Developers\r\nLevel: Beginner/Intermediate (No previous security experience required)\r\n\r\nTalk Summary:\r\n\r\nIn \"What's New in Android 15 Security?\", we’ll guide Android developers through the essential security updates and restrictions introduced in Android 15. Starting with a brief recap of the key security changes from Android 14, we'll dive into the new restrictions and features in Android 15 that every developer should know:\r\n\r\n- Supporting Android 15’s Private Space: Discover how to enable your app for this new feature to enhance user privacy while managing the challenges of a new type of process termination.\r\n- New Mitigation for Task Hijacking: Explore the new security measures designed to prevent malicious background apps from bringing other apps to the foreground.\r\n- Safer Intents: Discover enhanced safeguards that make intents more secure and robust, minimizing the risk of data leaks and unauthorized access.\r\n- Further Foreground Service Restrictions: Understand the tightened restrictions on foreground services and how to adapt your app to comply.\r\n- Recap of Privacy Sandbox Updates: Review the Privacy Sandbox and its updates in Android 15, focusing on how these changes impact user data handling and ad targeting.\r\n\r\n\r\nKey Takeaways:\r\n- Overview of Security Changes: Gain a clear understanding of the security updates in Android 15 and their implications for app development.\r\n- Practical Compliance Tips: Learn actionable strategies to ensure your app remains compliant with the latest security restrictions when running and/or targeting Android 15.\r\n- Leverage New Features: Discover how to take advantage of Android 15’s new security features to improve your app’s security and promote user privacy.\r\n\r\nSpeaker Experience:\r\nWith over 10 years of experience as a Senior Android Developer, co-authoring the Android Security Cookbook, and delivering talks internationally on Android security, I bring a deep understanding of the platform's security evolution. This will be a fresh and engaging talk for Droidcon London 2024.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ce5f643d-771a-4b14-b504-329c50924cc3", - "name": "Tarik Eshaq" + "id": "8b008502-24e7-4af7-a7a8-d92a7e683271", + "name": "Scott Alexander-Bown" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185719, - "name": "Other" - }, - { - "id": 185732, - "name": "Cross-Platform" + "id": 264372, + "name": "Security" }, { - "id": 185739, - "name": "Libraries" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529946", - "title": "Brick by Brick: Building Open Source libraries", - "description": "So you have an idea, and you want to publish a library for it? But where do you start? Doing Open Source is a fine art which requires skill you don’t easily learn on schoolbooks. Creating a new library is like building a new house that people want to live in: you need to start with a great foundation, build your inner walls and then add all the niceties that make your house the best place to live in. \r\n\r\nJoin us as we share our journey building Open Source Android libraries: we’ll start from tools to help you organize your code, we’ll learn how to publish your libraries publicly and how to effectively maintain them. Throughout this journey, we’ll share our experience maintaining popular Android libraries such as Detekt, Chucker and AppIntro.\r\n\r\nA house won’t be a home without someone living inside it, though. As your library won’t be a popular library without a strong community around it. So we’ll have the opportunity to share our insights on building strong communities around Open Source, getting developers together, finding new contributors and dealing with maintainer burnout.\r\n\r\nCurious to know how to build a shiny new library brick by brick? Then make sure to don’t miss out this talk, and we can’t wait to see what you all will be building!", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "693733", + "title": "Hardware development with KMP and Compose Desktop", + "description": "Nowadays, Mobile software development focuses on efficient architecture, scalability and delightful UX. Those are quite important topics but sometimes we forget Mobile Development has its roots in Embedded Software Development, which was that branch of software development focused on running code on limited resource devices.\r\n\r\nBoth have diverged, one specialised in mobile apps for smartphones, while the other has transformed more into IoT, industrial and other types of solutions.\r\n\r\nThere was an attempt to bring both together with Android Things. It made Android app development skills transferable for hardware development, letting developers create apps with their existing knowledge and interact with the hardware through “high-level” driver APIs. Unfortunately, it was decommissioned by Google. \r\n\r\nWith the flourishing of technologies like KMP and Compose Desktop, the regular Android developer again has some transferable skills that can be used to develop apps for non-smartphone hardware. \r\n\r\nIn this session I will talk about how to develop a simple app for devices like the Raspberry Pi, covering details like interacting with GPIO, WIFI and Bluetooth communication as well as current issues and workarounds. ", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8253d551-1437-4a3e-b4f4-56bf73373109", - "name": "Paolo Rotolo" - }, - { - "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", - "name": "Nicola Corti" + "id": "95a6035d-1167-4cc1-9974-bc065a077893", + "name": "Enrique Ramírez" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185735, - "name": "Android" + "id": 264361, + "name": "KMP" }, { - "id": 185739, - "name": "Libraries" + "id": 264370, + "name": "IoT" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528351", - "title": "Becoming and Staying a Productive Developer with Build Scans, Build Validation Scripts and Gradle", - "description": "We will start by discussing some of the top Gradle issues that affect the productivity of Android developers. We will cover build cache misses and configuration cache issues as well as how to debug and solve them with various tools such as with the free Build Scan service, Android Studio debugger and also with Develocity.\r\n\r\nWe will then demonstrate combining the powerful Build Validation Scripts with automation and notifications to systematically surface new build cache misses and to keep them from reappearing once they have been fixed.\r\n\r\nWe will close by diving into the just released Artifact Transform insights in the free Build Scan tool. This is one of the most commonly requested features by Android developers. We will also close off by showing how you can use the new Tests API in Develocity to automate your flaky test workflows.", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "711892", + "title": "Gradle: The Build System That Loves To Hate You", + "description": "Come to hear a talk about dozens of foot guns in Gradle that can make you tear your hair out as you try to \"just make it work\", presented by someone who has stepped on most of the rakes. For example, did you know every call in groovy is using reflection?!\r\n\r\nAlong with the issues you will also learn about way to avoid it or at least minimize your risk of misery. After the talk you should have more confidence as you venture into the land of Gradle build.", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "89694c9d-5de0-4f39-a50a-ed48d6eb56ca", - "name": "Nelson Osacky" - }, - { - "id": "67c6035b-3961-48b0-b4f8-49593995c5a0", - "name": "Etienne Studer" + "id": "8e2c2869-4beb-4868-8651-8567219ea947", + "name": "Aurimas Liutikas" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185729, + "id": 264380, + "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264378, "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "517498", - "title": "How Custom RenderObjects can make your life easier", - "description": "In Flutter we say: \"Everything is a Widget!\" and behind every visual widget there is at least one RenderObject.\r\n\r\nIf you already know the theory about RenderObjects, but you want to get your hands dirty and you don't know how to start, this talk is for you!\r\n\r\nDuring this session we will see in which use cases we want to create our own ones, which classes to use, how to understand how they work.\r\n\r\nI'm pretty sure that when we are not afraid of RenderObjects, they become our BFFFL (Best Flutter Friend For Life).", - "startsAt": "2023-10-26T10:30:00", - "endsAt": "2023-10-26T11:10:00", + "id": "764303", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T10:20:00", + "endsAt": "2024-10-31T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76117eec-d3f7-49c7-9684-c19058f241a0", - "name": "Romain Rastel" - } - ], - "categories": [ + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" + }, { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185700, - "name": "Intermediate" - } - ], - "sort": 0 + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" }, { - "id": 53528, - "name": "Session format", - "categoryItems": [ - { - "id": 185704, - "name": "Session" - } - ], - "sort": 1 + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" }, { - "id": 53529, - "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], - "sort": 4 - } - ], - "roomId": 39642, - "room": "Lamarr", - "liveUrl": null, - "recordingUrl": null, - "status": "Accepted" - }, - { - "questionAnswers": [], - "id": "538649", - "title": "Migrating to Jetpack Compose - an interop love story", - "description": "Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you then.\r\n\r\nWhile Compose-only is a dream experience, the reality is that existing apps will be mixed Views and Compose for a long time. The most common migration strategy is: all new features written in Compose, while old code remains in Views. \r\n\r\nDevs do refactor old code as well - but it’s not a must. You don’t need a Compose-only app to reap the benefits of Compose. So, let’s embark on a journey of partially migrating a View sample, to test a few hypotheses:\r\n\r\n- “Migration of common UI first” strategy\r\n- View and Compose interop capabilities\r\n- Migrating to Compose while keeping original navigation\r\n- Redesigning a feature/screen? Perfect time to migrate!\r\n- Migrating to Compose? Perfect time to add large screen support!\r\n- Views to Compose don’t always need a 1:1 mapping", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", - "isServiceSession": false, - "isPlenumSession": false, - "speakers": [ + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, { - "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", - "name": "Simona Milanovic" + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185741, - "name": "UI/UX" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "501854", - "title": "Developer Productivity On a Budget", - "description": "Adam and Jordan both work on Platform teams at different companies supporting many distributed teams that contribute to their Android codebases. \r\n\r\nWe'd like to share some of the things we've built for our Engineers to help them become more productive and ensure that they deliver great experiences for our users.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "764328", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:00:00", + "endsAt": "2024-10-31T11:15:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", - "name": "Adam Ahmed" + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" }, { - "id": "1849dced-2e7b-4d3a-b8f9-3c6e9a62df29", - "name": "Jordan Terry" + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185729, - "name": "Gradle" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529488", - "title": "A/B Test Best Practices and Fun Case Studies from Deliveroo", - "description": "At Deliveroo we run hundreds of A/B tests per year, and sometimes they return very unexpected results! Over the years, we’ve learned lots about running A/B tests, and today we’ll share some best practices for success. We’ll share some stories about some of the strangest results we’ve seen and by the end, we hope to convince you to start running A/B tests in your apps too! \r\n\r\nTopics we will cover:\r\nWhat is an A/B test?\r\nWhat are some of the most surprising results we have got from A/B tests at Deliveroo?\r\nWhat did we learn from those A/B tests?\r\nWhat are the best practices for running A/B tests?\r\nHow can I convince my boss that we should be running A/B tests at our company?\r\n\r\nCome along to find out if handling process death impacts how often people order food, and exactly how valuable your splash screen is!\r\n", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "742198", + "title": "Demystifying Code Signing", + "description": "All Android apps have to be code signed. This is performed either by the developer or through Google Play Signing. Despite this, code signing and the processes around it are shrouded in mystery.\r\n\r\nDrawing from lived experiences, this talk with cover:\r\n - What is a code signature?\r\n - Why do we need to sign in the first place?\r\n - Developer signing vs Google Play Signing\r\n - Security expectations vs reality\r\n - Going beyond signing", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "0ef1e55b-29b9-4c4b-938d-c59b6c8ea9d3", - "name": "Jamie Adkins" - }, - { - "id": "48a744a3-c5e5-40b7-8c64-eb134ff4dee7", - "name": "Edward Harker" + "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", + "name": "Neal Michie" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185719, - "name": "Other" + "id": 264372, + "name": "Security" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264379, + "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528793", - "title": "Sync Data with your Mobile Kotlin App The Easy Way!", - "description": "Part of building a great mobile app is syncing data between the app and your back end database. That used to mean creating and testing API calls, worrying about storing local data while offline, performing conflict resolution among multiple users, user authentication and authorization, network communications and error handling, and security. In this talk, I’ll show you how to address all of those issues effortlessly with MongoDB Atlas and Atlas for the Edge. Atlas is the Developer Data Platform from MongoDB – a multi-cloud database-as-a-service offering with state-of-the-art features to accelerate your development projects. Atlas for the Edge brings the power of MongoDB closer to your users at any location. Attendees will see how to build a mobile Kotlin app with the Atlas Edge SDK that easily connects with a MongoDB Atlas on the back end for fast bi-directional sync, out-of-the-box networking and conflict resolution, and built-in offline ACID-compliant data storage on the mobile device.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "735932", + "title": "Don't Build to Last: Strategies for Designing and Productionising Experiments", + "description": "Striking the right balance between rapid iteration and long-term feature quality can feel like an impossible task. In this session, we'll explore how to move fast without breaking things, and delve into the art of \"productionising\" experimental code.\r\n\r\nThe session will navigate the entire lifecycle of experimentation from an Android engineers perspective. You'll learn how to work with your data team to design high-quality experiments with tight feedback loops and how to effectively roll them out - whether that involves making new behaviour permanent or cleaning up the mess. This session offers practical insights for engineering teams looking to innovate quickly while maintaining a robust production environment.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "60004028-029a-4865-88eb-acd45debf033", - "name": "Mark Brown" + "id": "85f45c57-98a0-45e9-8a5f-ed6096e268b9", + "name": "Kate Moksina" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264380, + "name": "Tooling" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185744, - "name": "sponsor" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528157", - "title": "Offline First Architecture with Bloc and Reactive Database", - "description": "Building apps that work flawlessly with limited network accessibility is not trivial. However, with more and more demanding users it's a necessity. There can be several approaches to offline first architecture and in this talk we'll look into one of them. You'll learn how to use flutter_bloc for handling user's actions and data, how to enable database to host persistent queue of actions, and resolve other challenging topics when the app is offline.", - "startsAt": "2023-10-26T11:25:00", - "endsAt": "2023-10-26T12:05:00", + "id": "733188", + "title": "Improving App Performance With Custom Thread Pools", + "description": "In this presentation, I will discuss how to optimise the performance of an app by implementing a custom thread pool.\r\n\r\nThe audience will learn how to replace multiple thread pools created by libraries such as Coroutines, WorkManager, and OkHttp with a single, efficient custom thread pool. \r\n\r\nKey points include:\r\n\r\n1. Thread Pool Awareness: Understanding how various libraries create threads without awareness of existing threads and what the impact of that is on performance.\r\n\r\n2. Thread Pool Auditing: Demonstrating how to print to the LogCat a list of the currently running threads and showcasing a before and after.\r\n\r\n3. Performance Enhancement: Reducing RAM usage and improving startup times by consolidating thread pools.\r\n\r\n4. Custom Thread Pool Implementation: Demonstrating through code snippets how to create a Thread Pool and integrate it into OkHttp, Coroutines and WorkManager.\r\n\r\n5. Understanding The Differences Between Coroutines IO and Default Dispatchers: Using this knowledge to create a custom Thread Pool for IO work and CPU work.\r\n\r\nThis talk will provide concrete techniques for developers to enhance app performance, making it a standout topic for the conference.\r\n\r\nExperience Level:\r\n7 years experience as an Android Engineer, currently at Compare The Market for almost 4 years.\r\n\r\nI would rate myself as having intermediate knowledge on this topic, however it is a specialised topic.\r\n\r\nI have not given this talk anywhere, although I have presented this topic internally at Compare The Market.\r\n\r\nKeywords:\r\nasynchronous programming, thread pool, performance optimisation", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "29ba7916-20b4-48e5-9715-bddfbdd1aa3a", - "name": "Dominik Roszkowski" + "id": "e841b549-a549-420e-ba73-3039c567a609", + "name": "Tin Novakovic" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" + "id": 264369, + "name": "Other" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264384, + "name": "Android" }, { - "id": 185739, - "name": "Libraries" + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528710", - "title": "Go the extra mile for your media app on Android", - "description": "You have a great application that can read media like a video or audio, that’s great!\r\n\r\n\r\nBut maybe you can go a tiny bit further, to enable your user to have more features, and a more premium experience!\r\n\r\n\r\nA ton of tiny things are easy to do, and can leverage your app to go even further, for instance : \r\n* Downloadable content\r\n* Spatial Audio with Head Tracking\r\n* Enable HDR\r\n* Enable Picture in Picture\r\n* Support HALF_OPENED for Foldables\r\n* Support Android Auto, TV and Wear\r\netc..\r\n\r\nA lot of things to do that do not require a lot of development!\r\n\r\nThe goal of this session is to show you what you can add to your app media experience, and tell you how it’s easy to do!\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "756181", + "title": "Fortify the Fort: Think outside the security", + "description": "Protection is deliberately included in everything you do with Android. Privacy is enabled through Android security. Like any other developer, you need to follow a shared responsibility security model, meaning your customers and users play an essential part in the overall security posture. This talk shows how a developer can follow simple guidelines to improve the application's security and stop unauthorised access.", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f3facba0-c34d-4c87-a7e7-a81a67c8029d", - "name": "Antoine Danois" + "id": "4b141461-befd-4bd3-83c9-1c1f7599a9c6", + "name": "Goran Minov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185717, - "name": "Foldables" - }, - { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185719, - "name": "Other" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185730, - "name": "Android Studio" + "id": 264372, + "name": "Security" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530078", - "title": "Think outside the server", - "description": "Securing your server API is a common method to safeguard your Android application, however, it may not always be sufficient. In certain situations, you may need to have additional measures in place to protect the mobile app client beyond just server-side security.\r\n\r\nIn this presentation, we will explore a few of these scenarios and why they are so important to be aware of. In conclusion of this presentation, you will be able to identify, understand, and deal with the common client-side security challenges that you may encounter.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "735282", + "title": "Securing the Unseen: Advanced Threats, Best Practices & Challenges in Mobile App Security", + "description": "In today's app-driven world, securing mobile applications is not just an afterthought—it's a critical component of the development process. This session at DroidCon London will take you deep into the mobile app security world, focusing on the latest threats that target mobile applications and the advanced techniques used to secure them.\r\n\r\nWe'll explore cutting-edge security practices that should be integrated throughout the app development lifecycle, from secure coding standards to app security and comprehensive threat modeling. You'll learn to identify and mitigate vulnerabilities before they become exploits, ensuring your applications are resilient against modern attacks.\r\n\r\nWe'll discuss key risks, security techniques and challenges app developers and security engineers face when trying to secure code, keys, and data effectively.\r\n\r\nThis session is designed for app developers, security professionals, and penetration testers who want to stay ahead of the curve in mobile app security. Join us to enhance your skills and ensure your apps are secure in the face of today's and tomorrow's threats.\r\n", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5dd21448-f468-44ad-9220-75d25e8e2370", - "name": "James Hamilton" + "id": "eddfc3bb-2258-45f8-b4a6-50233932a2d2", + "name": "Mohamed Kerroumi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264363, + "name": "Testing" }, { - "id": 185722, + "id": 264372, "name": "Security" }, { - "id": 185724, - "name": "API" - }, - { - "id": 185728, - "name": "Modern Android Development" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528347", - "title": "Become a hero to your DevSecOps team", - "description": "Get in front of API Security. Mobile apps are the front line of your company’s customer engagement. With the correct architecture and security, they can also become the first line of defence in protecting your company’s infrastructure. This presentation will take you through a worked example showing how an Android app can strengthen an organisation’s cyber-defense.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "764329", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:15:00", + "endsAt": "2024-10-31T11:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f8c173b5-64f5-43e4-9f1c-ee44587b7691", - "name": "Neal Michie" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529968", - "title": "Remote Build Cache - overcoming the network latency tax", - "description": "Lengthy builds and tests are a tax on productivity, job satisfaction and CI infra costs. The solution is to modularise apps into sub-projects and connect build systems to a remote build cache. But in doing so how can we minimise the latency and bottlenecks of retrieving cache entries? This talk will show you how to turbo-boost your build benchmarks with remote caching.\r\n\r\n\r\n", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "764330", + "title": "Ask Android! - Compose, KMP, Gemini in AS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:35:00", + "endsAt": "2024-10-31T11:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3be14346-8241-4bd9-9a8e-6771276bc270", - "name": "Tamás Bazsonyi" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185729, - "name": "Gradle" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529504", - "title": "Supporting your architecture with code coverage and static analysis rules", - "description": "One challenge of engineering teams is how to ensure you're writing high-quality code. Code coverage has traditionally been one measure for this, but aiming for complete coverage across your codebase will rarely lead to meaningful results. \r\n\r\nIn this talk we'll look at how you can use inclusion and exclusion rules within your Android codebase to ensure that any measurements help enforce your architecture patterns, allowing engineers to focus on the code that matters and giving them confidence they're building in the right way.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "729194", + "title": "What’s New in Compose Multiplatform - A Live Tour", + "description": "What if you could just… do iOS development? Kotlin and Compose Multiplatform make it possible!\r\n\r\nLive coding our way through the evergrowing ecosystem built by JetBrains, Google, and the wonderful Kotlin community, we’ll show you how you can pick and choose well-established tools and libraries that you already know from Android and use them to build cross-platform apps.\r\n\r\nThis includes the addition of new Jetpack libraries such as Navigation, ViewModel, Room, and more – and you don’t have to make any compromises when it comes to using platform capabilities, either! Using JetBrains Fleet throughout the demos, you’ll also see the tooling support you get when developing multiplatform applications.\r\n\r\nYou may not realize it yet, but you probably already know how to build apps with Compose Multiplatform – for Android, iOS, and beyond.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3e36c7fe-b7ed-4a1a-b197-5937e0c611f6", - "name": "Michael Tweed" + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" + }, + { + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" }, { - "id": 185731, + "id": 264361, + "name": "KMP" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264380, "name": "Tooling" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "517620", - "title": "Stop Treating Accessibility as an Afterthought: Concrete Steps to Build Inclusive Apps", - "description": "\u2028\u2028In today's digital world, accessibility is more important than ever. However, accessibility is often treated as an afterthought in the development process, which can lead to exclusion and frustration for users with disabilities. This talk aims to change that by discussing how to make accessibility a core part of your development workflow. We will cover topics such as collaboration within your team, available tools and when it makes sense to use them, and modifications to your project to ensure adding accessibility is a pain-free experience for feature development and testing. By implementing these strategies, you can create a more inclusive product and provide a better user experience for all.", - "startsAt": "2023-10-26T12:20:00", - "endsAt": "2023-10-26T12:40:00", + "id": "760426", + "title": "Enter the Metaverse: Android Apps on Meta Horizon OS", + "description": "Meta Horizon Store is open for business to mobile developers. Android developers can now port existing mobile apps, easily add spatial capabilities and publish them for Meta Quest headsets. Start building for the next computing platform to target new users, innovate with mixed reality and grow your audience. With new tooling like Meta Spatial SDK, you can combine the rich ecosystem of Android development and the unique capabilities of Meta Quest via accessible APIs, all while using the mobile development languages, tools, and libraries you’re already familiar with.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "8e7913b6-fce4-419a-b862-7a1b535d1af9", - "name": "Manuela Sakura Rommel" + "id": "555e52b7-fcb6-49ea-81c7-d9b52cc4c7e9", + "name": "Jonathan Wendorf" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185714, - "name": "Accessibility" + "id": 264383, + "name": "AR/VR" }, { - "id": 185727, - "name": "Flutter" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538660", - "title": "Easy screenshot testing with Compose", - "description": "UI tests are an integral part of a good testing story, but they tend to be a source of flakes and can cause slow CI runs. With screenshot tests you can make multiple assertions at the same time, prevent regressions across multiple form factors and verify visual appearance of Compose UIs. In this talk we'll walk you through how screenshot testing works, how to manage golden images, how to create a strategy of what to test, which of the available solutions to go for to speed up your tests runs, and also we'll take a peek at the upcoming support in the Android Gradle Plugin and Android Studio.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "736303", + "title": "Text in Compose: Beyond the Basics", + "description": "In this talk, we'll explore the intricacies of text rendering in Jetpack Compose. We'll delve into text layout and various text APIs to create visually appealing and interactive text-based interfaces.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", - "name": "Jose Alcérreca" + "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", + "name": "Anastasia Soboleva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264364, + "name": "Compose" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" - }, - { - "id": 185741, - "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530107", - "title": "Threat Landscapes, Threat Models, and Threat Prevention: A Practical Guide for Android Developers", - "description": "Mobile apps are under constant attack. And the attackers' targets are not always the ones you might expect.\r\n\r\nJoin us for a whirlwind tour of the Android threat landscape, and discover some practical insights into what you can do to to fortify your apps:\r\n\r\n- Watch some real-time exploits of a demo banking app and start thinking more like an attacker\r\n- Get some tips on creating a threat model for your apps\r\n- Explore the protection measures that can help your apps to defend themselves", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "733000", + "title": "Exploring Android Accessibility Malware", + "description": "Join us in exploring two techniques Android malware uses, focusing on the dangerous combination of credential stuffing attacks and Accessibility Service abuse. We'll demonstrate how cybercriminals can exploit these vulnerabilities to launch large-scale attacks on user accounts across multiple applications.\r\nOur talk will walk you through:\r\n1. The mechanics of credential stuffing and how it exploits common user behaviors.\r\n2. How malware can abuse Android's Accessibility Service to automate malicious actions.\r\n3. A step-by-step demonstration of a proof-of-concept that combines these techniques.\r\n4. Clever methods cybercriminals use to conceal their activities from users.\r\n5. The broader implications of these threats for mobile app security.\r\n 1 of 3\r\nWe'll dive into why these attacks are increasingly prevalent and how they can be executed with alarming ease. By understanding the attacker's perspective, we aim to highlight the critical need for robust security measures in mobile applications. However, implementing such security measures can be challenging for developers, often requiring significant time, expertise, and resources. This is where innovative solutions become crucial. Recognizing this gap in mobile app security, Appdome provides comprehensive protection against these threats through zero-code integration, allowing developers to secure their mobile apps effortlessly.", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e98ce7fa-cb48-4eb6-8a60-be9d4b4c0b6a", - "name": "Ivan Kinash" - }, - { - "id": "e99fa2a9-4e01-4534-907d-00dbb1447360", - "name": "Ben Thompson" + "id": "36110e7b-c926-48ec-be43-b1a10e0641a0", + "name": "Gil Hartman" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" + "id": 264365, + "name": "Accessibility" }, { - "id": 185722, + "id": 264372, "name": "Security" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185744, - "name": "sponsor" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495541", - "title": "Composing ViewModels: Breaking ViewModels into smaller self-contained UI models", - "description": "In early times of Android development we used to have big activities. All view and business logic were being written in activities. Then fragments were introduced so that activities started to become leaner. Then several architectural patterns have started to appear in the Android scene: MVP, MVVM, MVI, etc. While all these were happening, ViewModel like classes started to get bigger and bigger. They have become the new big activities. However, we haven't often considered splitting view models into manageable self-contained granular ones.\r\n\r\nViews are composable and they can be composed to build bigger views. What about introducing smaller ViewModels, namely UI models, and using them to build composable UI models/View Models? \r\n\r\nIn this talk, we will introduce the micro-feature architecture that leverages granular UI models. We'll demonstrate how this approach enables us to create leaner UI hosts by breaking down view models into smaller, self-contained logical UI models. Moreover, we'll explore the distinctions between a UI model and a Jetpack ViewModel. \r\n\r\nWe'll delve into the advantages offered by micro-feature architecture, particularly in terms of single responsibility, composability, reusability, and testability. \r\n\r\nBy the end of the talk, you'll have a comprehensive understanding of how adopting this architecture can significantly enhance your development process and empower you to build high-quality, scalable applications which has highly dynamic contextual screens. \r\n\r\nMicro-feature architecture is used in several screens of 2 big apps in production which are now being used by 10M+ users. We will be also sharing our learnings about using this approach in these apps. ", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "702107", + "title": "[REDACTED]: How to keep your app's secrets, secret", + "description": "Every app has secrets! These could be in many forms (no judgment here!) but in this talk, we'll focus on the most common use case of storing API keys or data in-app that we'd rather not make readily available to malicious actors.\r\n\r\nIn this talk, we'll look at answering one of the most asked questions in mobile security, \"How do I secure my API keys\" and ensure you have the knowledge and tools you need to do so.\r\n\r\nYou'll leave this talk with:\r\n- Actionable security best practices for securing your app\r\n- An idea of how to store API keys, at scale\r\n- Multiple examples of how to securely access your API keys\r\n- Some fun real-world examples of when things go wrong\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5c3850ee-778a-4904-bd52-a3927ec54dc6", - "name": "Hakan Bagci" + "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", + "name": "Ed Holloway-George" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264369, + "name": "Other" }, { - "id": 185713, - "name": "Compose" + "id": 264372, + "name": "Security" }, { - "id": 185716, - "name": "Modularization" + "id": 264384, + "name": "Android" }, { - "id": 185735, - "name": "Android" + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "535307", - "title": "What's new in Paparazzi?", - "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this talk, John will give an overview of how it works, followed by an update on what the Cash App Android team has been working on since then, including:\r\n\r\n* Resource and asset loading improvements\r\n* Video snapshot support\r\n* Snapshots from @Previews\r\n* Compose Multiplatform support\r\n\r\n...and more!\r\n\r\nBackground:\r\n\r\nPaparazzi is an Android testing library that allows you to render your application screens without a physical device or emulator!\r\n\r\nIt has been immensely improving the Android UI testing loop on Cash App and other well-known apps since 2019 by allowing you to refactor your screens with confidence while running blazingly fast on the JVM versus navigating on slow devices.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "757537", + "title": "Roundtable: Mastering Release Management", + "description": "Teams of all sizes require release management processes that should ensure smooth delivery of app updates, while balancing the need for innovation with stability and user experience. Coordinating teams, managing release cycles, and mitigating risks while adapting to the Android ecosystem can be challenging. Join this open roundtable discussion to examine:\r\n•\tWhat are the biggest challenges in managing releases within the Android ecosystem, especially with the variety of devices and OS versions?\r\n•\tHow do you allow frequent releases while minimizing the toil of release management?\r\n•\tHow do you ensure a smooth release process while balancing the need for continuous innovation and feature updates?\r\n•\tWhat are some best practices for testing and quality assurance before rolling out an Android release to a large user base?\r\n•\tHow do you manage the potential impact of an unsuccessful or buggy release, and what steps are taken to mitigate such risks?\r\n•\tWhat role do user feedback and crash analytics play in refining subsequent Android releases?\r\n", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", - "name": "John Rodriguez" + "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", + "name": "Maria Neumayer" + }, + { + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185730, - "name": "Android Studio" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "517499", - "title": "Exploring Records and Patterns", - "description": "Dart 3 is packed with new experimental language features, including Records, Pattern Matching, Algebraic datatypes, and Destructuring. Like Extensions, they’ll open up new opportunities for packages and ways to express oneself in code. Through concrete examples, Pascal will guide you through the syntax of these new features and demonstrate how they can be used to create more expressive and maintainable code.\r\n\r\nYou’ll learn about:\r\n\r\n- The benefits of the new Dart 3 language features\r\n- The considerations in designing these features\r\n- What the syntax looks like in isolation\r\n- New patterns and anti-patterns and how they will shape our Dart code of tomorrow\r\n\r\nWhether you're a seasoned Dart developer or just getting started, this talk will help you get up to speed on the latest advancements in the language. Don't wait for the future to arrive – start exploring these new features today and take your code to the next level.", - "startsAt": "2023-10-26T13:50:00", - "endsAt": "2023-10-26T14:30:00", + "id": "764323", + "title": "Ask Android! - Compose, KMP, Gemini in AS (copy)", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T11:50:00", + "endsAt": "2024-10-31T12:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f117aa78-7e11-451d-b7ba-fb19ac866f31", - "name": "Pascal Welsch" + "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", + "name": "Sebastiano Poggi" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529442", - "title": "Advanced Android Canvas APIs", - "description": "Dive into the Android Canvas APIs, the underlaying Skia engine and how it powers both native Android and Jetpack Compose UIs. This talk offers a deep dive into drawing primitives, transformations, and custom views, showcasing how to craft dynamic and visually engaging user interfaces. Join me to uncover the secrets of advanced Canvas techniques that will elevate your app's visual appeal and user experience.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "764331", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T12:30:00", + "endsAt": "2024-10-31T13:40:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "71373c0f-53e8-4020-8d16-1bdfe603c3c7", - "name": "Markus Hintersteiner" + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "31e8a089-eb2f-437c-ac09-bb55af17f934", + "name": "Donovan McMurray" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185741, - "name": "UI/UX" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530222", - "title": "App architecture - singular vs plural", - "description": "A single architecture is usually at the core of our apps. We build our app and its features around it and by its rules. And while our chosen architectural pattern might work, and even work well, do we really benefit from a \"one per app\" approach? \r\nIn this talk, we'll do a quick run-through of the most popular architecture patterns in Android and try to figure out if it's better to strive for a singular architecture per app or if we should consider different ones for different features.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "728763", + "title": "Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!", + "description": "Let's have fun with animations, gestures and sensors!\r\n\r\nUsing Compose Multiplatform, we'll go over how to create animations using gestures and sensor events for Android & iOS. We'll cover some basics like how to get the device motion and position information, how to track gestures in the screen, and how you can combine them with animations to have fun! \r\n\r\nAfter this talk, you'll have a better understanding on how to use the sensor frameworks, how to make your own gesture effects, and how to create interesting animations in an easy way.\r\n\r\nKeep it fun, keep it animated!", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "163e9ce7-9042-4482-8f9e-f713d92ceae4", - "name": "Yoni Tietz" + "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", + "name": "Nicole Terc" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185716, - "name": "Modularization" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264359, + "name": "Kotlin" }, { - "id": 185719, - "name": "Other" + "id": 264364, + "name": "Compose" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "502453", - "title": "I have a Date", - "description": "One common task in Android app development is working with Date object, and Kotlin provides several options for managing it. \r\n\r\nIn this talk, we will explore the different ways to represent and manipulate dates in Kotlin, including using the built-in java.util.Date class, the third-party Joda-Time library, and the java.time package introduced in Java 8. \r\n\r\nWe will also discuss best practices for formatting and displaying dates to the user, and handling time zones and daylight saving time. By the end of this talk, attendees will have a strong understanding of how to effectively manage dates in their Kotlin Android apps.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "716104", + "title": "Swift Cheatsheet for Android/Kotlin Developers", + "description": "Knowing common Swift patterns and how they translate to Kotlin can help us understand better what the code does. Whether to see how some feature is implemented on the neighbor platform, perform code reviews, review or write tech specifications/proposals, or work with Kotlin Multiplatform.\r\n\r\nWe will go over some of the basics of the Swift language and how it compares to Kotlin. Additionally, we will cover common patterns that you might find in a typical iOS project like optional bindings, dictionaries, extensions, structures, and protocols.\r\n\r\nLeave this talk confident you can read, understand, and review Swift/iOS code. It will also help you start with Kotlin Multiplatform where knowledge of Swift and SwiftUI is important.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "4674e36f-50f8-4ffb-a486-679d730c759e", - "name": "Renaud Mathieu" + "id": "c24d26ff-8a59-4954-b768-fcd09d8bb0a3", + "name": "Domen Lanišnik" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "527630", - "title": "White-box Cryptography", - "description": "Dive into the world of white-box cryptography and discover its superior data protection compared to traditional methods. \r\n\r\nLearn how it guards against a range of threats including reverse engineering, and other common threats to applications. \r\n\r\nJoin us to elevate your security expertise and stay ahead in the ever-evolving landscape of data protection.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "748950", + "title": "How to Optimize, Validate and Deploy ML Models On Device", + "description": "In this workshop we address the common challenges faced by developers migrating AI workloads from the cloud to edge devices. Qualcomm aims to democratize AI at the edge, easing the transition to the edge by supporting familiar frameworks and data types. ​\r\n\r\nWe'll talk through why ML is best done on device and how to easily select a model for your use case, train (or fine-tune), and then compile for the device of your choice.\r\n\r\nWe'll walk through how to get started, iterate on your model and meet performance requirements to deploy on device! We'll show examples on how to optimize models and bundle the model into your application.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c746f9bf-2963-4549-b2f8-26cdafa24936", - "name": "Oliver Williams" + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264355, + "name": "Workshop" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185739, - "name": "Libraries" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "540429", - "title": "REST in Peace: A Journey Through API Protection", - "description": "Isn't Droidcon a Mobile Developer's conference? So, why would I care about protecting REST APIs? Well, think twice! API protection starts in the app, as the protection level of the API will be only as strong as the protection mechanisms built in the app. Join us for this entertaining talk were we will cover the typical mechanisms used to protect REST APIs and how they can be \"exposed\" if insufficient protection is put into the application. And, of course, guide you into putting the right measures inside the app so that both, app and backend, are sufficiently protected.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "725157", + "title": "Click-free releases & the making of a CLI app", + "description": "At TravelPerk, we've developed a tool that fully automates our release process, eliminating the need for human intervention. This was achieved using a lightweight Kotlin CLI app. In this talk, we'll provide an in-depth look at the design and implementation of this tool, and guide you through building your own Kotlin CLI app. Join me to explore the power of Kotlin and Gradle in creating robust, automated solutions.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "377a3c7a-eba5-48e0-9057-0a5ff91061cc", - "name": "Marc Obrador Sureda" - }, - { - "id": "c6100c4b-13c4-433e-8dcd-ea0c92939179", - "name": "Andreas Luca" + "id": "3b1289fc-f706-4f8f-9061-c77e3e250223", + "name": "Adam Ahmed" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264359, + "name": "Kotlin" }, { - "id": 185724, - "name": "API" + "id": 264380, + "name": "Tooling" }, { - "id": 185744, - "name": "sponsor" + "id": 264394, + "name": "CI/CD" + }, + { + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529978", - "title": "Building RTL-Ready Flutter Apps", - "description": "Are you looking to expand your app's reach to the global market and support RTL languages? Building RTL apps can be complex, but it's essential to ensure a seamless experience for millions of Arabic, Hebrew, and Persian-speaking users.\r\n\r\nThis talk will cover Flutter's RTL implementation and internationalization support. You'll learn about important widgets like Directionality, TextDirection, MediaQuery, and EdgeInsets that are crucial for creating RTL-friendly user interfaces.", - "startsAt": "2023-10-26T14:45:00", - "endsAt": "2023-10-26T15:05:00", + "id": "756374", + "title": "Tools that could help you to target newer SDK level and improve compatibility", + "description": "It could be a painful journey when developers update the app's target SDK level. There could be new APIs, deprecation of current APIs and gated or non-gated change. \r\n\r\nThis session will share major changes in Android 15, the common mistakes when targeting the latest version of Android, why developers should target the latest version as soon as possible, and what tools are available to help developers.", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "cae62422-4040-4130-b345-29c8cd65f41b", - "name": "Raouf Rahiche" + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185714, - "name": "Accessibility" + "id": 264363, + "name": "Testing" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264384, + "name": "Android" }, { - "id": 185727, - "name": "Flutter" + "id": 264379, + "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "508933", - "title": "Meet Jewel: create IDE plugins in Compose ✨", - "description": "Jetpack Compose is the declarative UI toolkit for Android that makes it easy to create beautiful, responsive apps. However, until recently, there was no easy way to use Compose to create IDE plugins without too many compromises. Jewel is a new library that solves this problem by providing a set of tools and components that make it easy to create Compose for Desktop-based plugins for Android Studio and IntelliJ IDEA.\r\n\r\nIn this talk, we will introduce Jewel and show you how to use it to create your own IDE plugins. The session will cover the following topics:\r\n\r\n* Why Compose makes a huge difference in IDE plugin development\r\n* How to set up Compose in your IDE plugin project\r\n* What's Jewel, a library implementing the IntelliJ themes and a composables\r\n* What are the tradeoffs in using Compose and Jewel\r\n* Case studies of existing implementations\r\n* What's the future for Jewel and Compose as a plugin UI framework\r\n\r\nThis talk is intended for Android developers who are familiar with Compose but have never created an IDE plugin because they don't wanna learn Swing. By the end of this talk, you will have a good understanding of how to use Jewel to create your own Compose-based IDE plugins.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "757518", + "title": "Roundtable: EM or IC? What’s Right for Me?", + "description": "Choosing between a career path as an engineering manager or an individual contributor is a pivotal decision for engineers as they advance in their careers. This choice often hinges on personal interests, leadership aspirations, and the desire for hands-on technical work. Let’s get together and discuss:\r\n•\tWhat are the key factors that developers should consider when deciding between becoming an engineering manager or staying on the individual contributor track?\r\n•\tHow can someone assess whether they have the necessary skills and mindset to transition into a management role?\r\n•\tWhat are the most significant challenges and rewards that come with each path—engineering management versus individual contribution?\r\n•\tHow can companies support employees who are undecided or want to explore both career paths simultaneously?\r\n", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e397fee8-078f-4694-8a1f-35b1d57f1fce", - "name": "Sebastiano Poggi" + "id": "af079576-c7db-497a-990d-919fc2472e45", + "name": "Anastasia López" }, { - "id": "eb4483f1-306b-447d-9888-648ec0fc0018", - "name": "Chris Sinco" + "id": "f40ca183-824d-43d9-9e63-ee0edb5aa307", + "name": "Ataul Munim" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185730, - "name": "Android Studio" - }, - { - "id": 185741, - "name": "UI/UX" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "524023", - "title": "Working with custom Android devices", - "description": "Android is now well established outside the world of smartphones. Although usually not officially supported, you can find Android running on all kinds of devices, like home appliances, digital signage, and point-of-sales terminals. Being Android developer today means you are likely to encounter these custom devices in your career.\r\n\r\nIn this session you will learn the most important parts of working on these devices. We will cover things like developers boards, board support packages, AOSP builds, firmware updates, and much more.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "764343", + "title": "Ask Android! - Compose, GenAI, WearOS", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T13:40:00", + "endsAt": "2024-10-31T14:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "79823093-1b16-4a2b-b0a9-ea4cd25b92f5", - "name": "Erik Hellman" + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528396", - "title": "Android App Performance in a Nutshell", - "description": "This session will give you a head start with actionable advice on the journey to Inspect, Improve and Monitor your app's performance with the Android Performance development process. You'll learn the latest in Baseline Profiles, Perfetto Tracing and Benchmarking.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "764345", + "title": "Ask Android! - Compose, GenAI, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:20:00", + "endsAt": "2024-10-31T14:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e869461e-b6fb-43e5-a89c-033da87008c0", - "name": "Ben Weiss" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185730, - "name": "Android Studio" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495134", - "title": "How to stop the ‘Gradle Snatchers’: Securing your builds from baddies", - "description": "Following on from one of the first recorded supply chain attacks against Gradle, this talk will discuss the security concerns surrounding our favorite build tool and how we can protect against them. This starts with gaining an understanding of some of Gradle's common vulnerabilities and how to avoid these within our Android projects. You'll leave this talk with:\r\n\r\n- Insights on the Gradle Wrapper supply-chain attack and how to protect against it.\r\n- An overview of a Gradle dependency attack and how to protect against them.\r\n- A concrete list of security setting best practices within Gradle, including wrapper verification, repository filtering, dependency verification and others.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "734739", + "title": "Video Editing on Android with Media3", + "description": "Learn how to use Media3 Editing libraries to edit, trim, concatenate and apply effects to video frames on Android. \r\nImplementing video editing has always been a challenge for Android developers. Media3 is a jetpack library that is meant to make video editing easy, performant and reliable. \r\nIn this session you will learn about Transformer APIs, how to apply effects and concatenate multiple media files. ", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f7ad854d-1167-4fab-be0a-2721cf6ae5f2", - "name": "Ed Holloway-George" + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185722, - "name": "Security" + "id": 264374, + "name": "API" }, { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185729, - "name": "Gradle" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "510229", - "title": "Conducting tech interviews", - "description": "Conducting proper tech interviews are hard. It has taken me more than 20 years of interviews, both as a candidate and as an interviewer, to come up with a process and key aspects that I try hard to go through before hosting an interview. \r\nI've also learned as a candidate signs of an unhealthy company and a few take aways that I can apply to when I'm interviewing myself. Both positive and negative.\r\nIn this session I will share my history of technical interviews, what I look for when interviewing, how I want to set the scene, and how I try to always get the best out of each interview. Hopefully you can take away some learnings and inspiration for your own interviews after hearing me out. ", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "732898", + "title": "Your app could use a secondary screen!", + "description": "Learn how an Android application could be used in a desktop-style environment while leveraging a secondary screen. We'll go through some key technical aspects to consider while developing an enterprise-oriented application and how Zebra is helping the developers go beyond what Android already offers.\r\n\r\nKey takeaways:\r\n- How an Activity is getting Multi Window support\r\n- Lifecycle impacts while using the application on 2 screens\r\n- Working with DisplayManager APIs to launch your activity on a secondary screen", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bbcf99f4-d0a2-4081-afe8-114e09f54a86", - "name": "Bob Dahlberg" + "id": "de4da391-af75-405b-8816-186c5d4e8fde", + "name": "Daniel Neamtu" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, + "id": 264349, "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264353, + "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530253", - "title": "Google Home, But Better: Smart Home Display with Flutter", - "description": "We may not have realized it yet, but Flutter could become the next big player on embedded devices.\r\n\r\nWe'll take a dive into running Flutter on embedded Devices and build our own Smart Home Display using Flutter, a \r\nRaspberry Pi and embedded Linux.\r\n\r\nWhat's currently supported and what custom Embedder can we use to achieve this? How do we communicate between Flutter and the embedded hardware? How can we integrate and use Flutter in the Maker-Community to open up to the world of IoT?\r\n\r\nThis talk aims to show the potential of Flutter on embedded devices, to give a practical guide on how to start developing Flutter with a Raspberry Pi on embedded Linux, and to showcase a project where Flutter connects to the Internet of Things.", - "startsAt": "2023-10-26T15:20:00", - "endsAt": "2023-10-26T16:00:00", + "id": "768482", + "title": "How to Optimize, Validate and Deploy ML Models On Device (Part II)", + "description": "We'll walk through the steps to bring your ML model on device. In this hands on section of the workshop we will demonstrate the end to end workflow for a sample use case, using Qualcomm AI Hub to optimize a model and deploy it on device. \r\n\r\nWe'll then help you get set up and walk through various examples on how to use Qualcomm AI Hub. The Qualcomm AI Hub team will be there to teach you the ins and outs, enabling you to use the platform and bring your ML use case on device quickly and easily.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e6858863-4e16-44ab-b2e0-22c940533654", - "name": "Moritz Theis" + "id": "0730a976-72e0-42c9-a3b3-9317c06d7bbe", + "name": "Bhushan Sonawane" + }, + { + "id": "b32c51bc-deaf-43a9-9715-099e5e18f692", + "name": "Meghan Stronach" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 264355, + "name": "Workshop" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185720, - "name": "IoT" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" + "id": 264382, + "name": "AI/ML" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "516527", - "title": "The terminal is your friend", - "description": "Command line skills are something we sometimes overlook as Mobile Engineers. Fancy tools and IDEs have made our lives very comfortable. We don’t need to spend a significant portion of our time writing commands in our systems like previous generations of Software Engineers and System Administrators.\r\n\r\nYet there are times when either the IDE has decided to go nuts or your version control tool is not syncing properly and you want to go one level down to see what is going on. Or maybe you just simply want to develop your CI skills which are heavy script and command-line based. \r\n\r\nIn this talk, I want to share some tips and tricks to help you level up your command-line skills and make the console a companion for your daily Android work.\r\n", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "734309", + "title": "Crafting Narratives: Shaping TalkBack with Compose Semantics", + "description": "\r\nThe focus of my talk will be on making modern, complex designs more intuitive for screen reader users using an empathetic approach . \u2028We will see how to create thoughtful and user-friendly narratives for accessibility by using Jetpack Compose semantics and optimizing the TalkBack experience.\r\n\r\nKey Takeaways:\r\n1. Creating Effective Narratives: Tips for simplifying complex UI elements.\r\n2. Testing Accessibility: Methods for validating and improving semantic changes.\r\n3. Designing for Complexity: Making complex UIs intuitive and accessible.\r\n4. Using Semantics Wisely: Enhancing accessibility with thoughtful design.\r\n", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "95a6035d-1167-4cc1-9974-bc065a077893", - "name": "Enrique Ramírez" + "id": "bcf3c75a-e105-41de-8891-53f6b673e58a", + "name": "Sachin Sapkale" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185731, - "name": "Tooling" + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264365, + "name": "Accessibility" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "516305", - "title": "Designing for Errors in a Kotlin-first SDK: When to throw, nullify, or return a Result.", - "description": "Error propagation is an important consideration for building feature-rich API architecture in Kotlin. Whereas apps may have the flexibility to add external dependencies and laser-focus on specific use-cases, designing an API requires minimal dependencies and wide applicability. This lightning talk is a crash course in Kotlin error propagation strategies and a case study in our experience designing errors when migrating our SDK from Java to Kotlin. We will go over the various techniques that Kotlin natively provides for us, including throwing exceptions, returning null, sealed classes, and Result, and how we decided which technique to use for our functions.", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "736362", + "title": "Streamlining Android app development and release processes", + "description": "Building, testing and releasing Apps for Android is unique with it's own challenges.\r\n\r\nDuring this session we’ll walk through three ways to make building, testing and releasing apps faster and easier for your whole team. Specifically, we’ll look at these three key points:\r\n\r\nAutomating testing and debugging by using CI/CD best practices and out of the box integrations.\r\n\r\nDelivering faster green builds with Bitrise Build Cache. We’ll look at some benchmarks and some real world examples of optimizing the execution.\r\n\r\nFrom beta distribution to production rollout, what steps are there and how these can be optimised.\r\n\r\nBy the end of this talk you'll have an idea on how to take the next steps on your CI/CD journey.", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5b17491b-317a-4fec-8b63-3264bc0a9cb9", - "name": "Hudson Miears" + "id": "3be14346-8241-4bd9-9a8e-6771276bc270", + "name": "Tamás Bazsonyi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, + "id": 264353, "name": "Lightning talk" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185724, - "name": "API" + "id": 264380, + "name": "Tooling" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264394, + "name": "CI/CD" }, { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185736, - "name": "Design" - }, - { - "id": 185739, - "name": "Libraries" + "id": 264378, + "name": "Gradle" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530221", - "title": "Structuring modules in an Android app project", - "description": "Let us suppose we have a multi-module project that builds faster than a monolith. \r\nWe have embraced separation of concerns and can now test a self-contained module without mocking the universe. \r\nWe are impressed with modularization, so we create more modules. \r\nThe question now is: How do we organize these modules?\r\n\r\nIn this session, we’ll dive into how many modules are too many in an Android app project and whether there can be too many. You’ll leave this talk with an understanding of how we can improve Android app modularisation to increase developer productivity, organize a clear project structure, access modules faster and assign ownership for better workflows. ", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "764347", + "title": "Ask Android! - GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:35:00", + "endsAt": "2024-10-31T14:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c35f2359-2c5c-4b88-a4bc-024a6c025ce4", - "name": "Kinnera Priya Putti" + "id": "81ee4554-4839-4192-9a74-ecb32dff2819", + "name": "Alex Vanyo" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "512823", - "title": "Latest State of Flutter Feature for Developing Multiple-Entry Android Application", - "description": "Exploration of latest state of the functionality in Flutter for developing multiple entry Android application. The feature allows developers to build single application with multiple entry points to gate/isolate specific features under exact entry, what can be useful for specific cases, like building POS-terminal app, where custom launcher is used for opening specific features of the app.\r\n\r\nKey takeaways:\r\n- From experiment to release\r\n- When and why to build Android version of Flutter application with multiple entry points.\r\n- How to make it work on Android\r\n- Potential limitations of this feature", - "startsAt": "2023-10-26T16:15:00", - "endsAt": "2023-10-26T16:35:00", + "id": "764350", + "title": "Ask Android! - Gen AI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T14:55:00", + "endsAt": "2024-10-31T15:10:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "606a1cb6-edd2-42bd-8bd4-08a51ef41c7f", - "name": "Vadym Pinchuk" + "id": "327f56b4-c631-4489-9590-37bdfc856e3a", + "name": "Miguel Montemayor" + }, + { + "id": "ecd35d14-91dc-4ce6-a6db-fd7a26424a36", + "name": "Chris Assigbe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e3b228b6-3b08-4edb-89dd-1a5625d9e4c2", + "name": "Francesco Romano" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185735, - "name": "Android" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538657", - "title": "Enhancing Jetpack Compose App Performance: Tools, Strategies and Optimizations", - "description": "Explore techniques to elevate Jetpack Compose app performance through measurement, debugging, and strategic optimization. Harness the capabilities of sophisticated tools, including Macrobenchmark and Android Studio's profiling suite, to pinpoint bottlenecks. Delve into the implementation and debugging of Baseline Profiles, which mitigate the impact of JIT on your code execution. Additionally, learn strategies to alleviate heavy UI rendering, ensuring a smoother user experience. Join us to gain actionable insights and practical skills for crafting high-performing Jetpack Compose apps that deliver seamless user experiences.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "710572", + "title": "Rethinking Home - How testing techniques and code design reshaped the new Spotify Home feature", + "description": "Let's dive into the dynamic world of Spotify Home development, where scalability and excellency are key. In this talk we will learn about the relationship between good code design and testability. How these principles synergise to influence the new shape of core Home features. Moreover we will go through the value of different test types and learn insights on how to make code more testable as well as actionable strategies to enhance code design in general ", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c95ac59a-7c1e-45b6-ba71-cf6ed861ac4a", - "name": "Tomáš Mlynarič" + "id": "6ee5ee5a-1bdd-4c54-8178-fad51f86a78a", + "name": "Daniel Horowitz" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264368, + "name": "Techniques & Guides" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530024", - "title": "Put Your Tests on a Diet: Testing the Behavior and Not the Implementation", - "description": "How do you write tests? How much time do you spend writing tests? And how much time do you spend fixing them when refactoring?\r\n\r\nA few short years ago, we would test a class in JUnit by stacking one test after the other, mocking each one of its dependencies. But for large apps, writing tests in this way without any consideration of architecture can easily become a bottleneck.\r\n\r\nFor a task that takes so much of our time every day as developers, there is surprisingly little discussion online about optimal test design. At Perry Street Software, publisher of two of the world's most popular LGBTQ+ dating apps, after struggling writing and maintaining our unit tests in the past, we have spent much time developing what we like to call the Unit Testing Diet.\r\n\r\nOur Unit Testing Diet provides a healthy way to test and refactor an app at scale, by testing the behavior that a user performs and the outcome that they will perceive, without testing implementation details.\r\n\r\nJoin this talk to learn how to:\r\n\r\n1. Test your ViewModels, UseCases, and Repositories in a Given-When-Then style, without using mocks\r\n2. Test the behavior and not the implementation details\r\n3. Refactor your code without breaking your tests", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "751161", + "title": "Designing scalable Compose APIs", + "description": "As more and more apps and teams migrate to Compose, it's important to establish clear guidelines for writing high-quality Compose code. This talk will cover best practices and guidelines for developing idiomatic Compose APIs, with topics such as how to think about and plan for your components, how to leverage Kotlin and naming conventions, and define a solid structure of your component, and finally how to verify and maintain these APIs. We'll discuss the rationale behind these guidelines and how they can help developers write code that is more scalable, performant, and consistent.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "85d2aab0-cf59-492c-83fb-ff0a8490fb85", - "name": "Stelios Frantzeskakis" + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264359, + "name": "Kotlin" }, { - "id": 185718, - "name": "Techniques & Guides" + "id": 264364, + "name": "Compose" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "517414", - "title": "And Gradle says: sharing is caring - Or why Gradle Plugins are all you need for your Configuration", - "description": "Have you ever been in dependency hell? Are you tired of copying and pasting your setup from one project to another? Do you wish there would be an easy way to share your configurations, workflows, dependencies? Say no more!\r\n\r\nBorn out of painful lessons, this talk will give you a crash course in how you can ship your setup easily to different projects by using the power of Gradle (Convention) Plugins, VersionCatalogs, etc. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "745796", + "title": "Securing Android: Tackling Advanced Threats and Enhancing App Security", + "description": "What threats are Android apps dealing with these days? In this talk, we will look at the latest security challenges and the best ways to keep your apps safe from new threats.\r\nWhen companies rush to release apps, they often skip important security steps, which can lead to higher costs and more risks. It's important to build security into your app from the beginning. We will discuss common risks for Android apps, how to handle them, and the challenges developers and security experts face when trying to protect code, keys, and data.\r\n\r\nKey Takeaways:\r\n\r\n* Get up to speed on the latest security threats specifically targeting Android apps.\r\n* Explore the latest advanced rooting technique such as Magisk and app instrumentation and the best way to protect your app.\r\n* Explore the differences between shrinking/minification and advanced techniques for obfuscation and anti-tampering, including cryptographic key protection.\r\n* Discover how to embed security throughout the development process to minimise risks and build resilient apps.\r\n\r\n\r\n", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "24ce44ea-d436-4476-88ac-08695de0acfa", - "name": "Matthias Geisler" + "id": "24eb82e3-3ea7-4b45-9ffb-d63cac580e88", + "name": "Mohamed Kerroumi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185729, - "name": "Gradle" + "id": 264372, + "name": "Security" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530160", - "title": "From Complex to Seamless - Succeeding in codebase migrations", - "description": "Complex changes require careful planning and execution - from gathering product requirements and system design to implementation. Introduce variables, like timezone differences, varying levels of technical and domain knowledge and a newly formed team and the complexity deepens. \r\n\r\nAt Skyscanner we were faced with all of these difficulties in our migration of our Hotels product from React Native to Native. This talk will take you through our journey and how we not only overcame these hurdles, but leveraged them to our advantage. ", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "757989", + "title": "Rethinking Apps with Local-First Architecture", + "description": "Modern apps are expected to function flawlessly even in low-connectivity edge environments, but traditional centralized networking approaches often fall short. In this session, we’ll explore how we got here and where we’re headed, diving into the unique challenges of local-first architecture. We’ll look at some of the tough problems in this space and examine solutions that open new possibilities. By rethinking these aspects in your app, you can push the boundaries of what your app can do and create experiences that will amaze your users.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3d4b5098-0829-4b34-b0bf-122ab126fdae", - "name": "Maria Neumayer" + "id": "da380fb8-c8ba-47e9-b224-26647296cff5", + "name": "Richard Das" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" + }, + { + "id": 264369, + "name": "Other" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264388, + "name": "Libraries" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "504429", - "title": "Deep-Dive Into Mobile Accessibility & Interactive Quiz", - "description": "Nowadays mobile apps are at the heart of our interactions with the world around us. But are we sure that our apps are usable for everyone ? As developers, how can we make sure that the experience of our screen reader users is the best ?\r\n\r\nDuring this presentation, you will compete with the audience to measure your mobile accessibility knowledge, and learn at the same time how to optimize your application user experience for screen readers. \r\n\r\nWe will go deeper than the usual contentDescription to improve accessibility. For example by using Custom Action or Long Click to reduce the number of Talkback gestures needed to go through one screen. We will also open doors to new gestures to improve the UX of infinite horizontal lists like in Spotify or Netflix, that can be a trap for screen readers. \r\n\r\nPrepare yourself to learn new tricks that will make the usability of your app better for user with a screen reader than for sighted users.\r\n\r\nBonus 🎁 : The highest score to the quiz in the audience will win a small prize.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "709487", + "title": "Dynamic Flow, the Wise approach to server driven UI", + "description": "In this presentation I will talk about how we combined the expertise of Android, iOS and Web engineers to create a server driven UI solution using Kotlin Multi Platform (KMP). This approach has allowed Wise to efficiently scale our product and business, resulting in more than 500 unique native screens being generated by the backend. \r\n\r\nLearn how this project has empowered backend engineers, streamlined UI development process and enhanced cross-platform collaboration.", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "0e28eaf0-f8b6-4475-a3be-f3ce49f2d1ec", - "name": "Fanny Demey" + "id": "f5e37102-a8be-4e34-be47-b98329cd2a23", + "name": "Phellipe Silva" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264361, + "name": "KMP" }, { - "id": 185714, - "name": "Accessibility" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "517710", - "title": "Introduction to Game Development with Flutter and Flame", - "description": "This session will be purely based on Game Development in Flutter with help of Flutter. \r\nI will start with a basic introduction to the Flame engine and some of the concepts of Game Development like Folder Structure, Game Loop, Collision Detection, adding objects, and giving a piece of awesome music to our game. \r\n\r\nIt will be a Shooting game or a Platformer game, but it will cover introductory to intermediate topics of Game Development in Flutter with Flame Engine. \r\n\r\nThis will really help users to explore the Game Development sector of Flutter and Flame.", - "startsAt": "2023-10-26T16:50:00", - "endsAt": "2023-10-26T17:30:00", + "id": "768515", + "title": "Kotlin by JetBrains, present and future", + "description": "Have a chat with Developer Advocates from JetBrains about anything Kotlin related on your mind. Language evolution, tooling, development practices, Kotlin Multiplatform and Compose Multiplatform... We're here for all of it!", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "d3469e78-7d62-4945-be1f-b9b3cc4a134b", - "name": "Shree Bhagwat" + "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", + "name": "Márton Braun" + }, + { + "id": "a82c6942-6e2b-4d80-9883-0b84b932d6ef", + "name": "Sebastian Aigner" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185732, - "name": "Cross-Platform" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538663", - "title": "Fireside Chat with Googlers", - "description": null, - "startsAt": "2023-10-27T09:00:00", - "endsAt": "2023-10-27T09:50:00", + "id": "764353", + "title": "Ask Android! -GenAI, Platform, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:10:00", + "endsAt": "2024-10-31T15:50:00", "isServiceSession": false, - "isPlenumSession": true, + "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185706, - "name": "panel" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "524652", - "title": "Peeling Back the Layers: Unmasking the UI-nknown!", - "description": "How much do you know about the UI layer and its best practices? What's the preferred way to produce UiState? How to consume it? Should you use MVVM or MVI? What about handling UI events? Where to hoist a particular state? What types of state holders can you have in your app? Should the ViewModel contain all the logic that comes from the UI? What are its limits?\r\n\r\nAll these questions and more about the UI layer will be answered in the talk. Level up your UI layer knowledge and make it a well-known area. We'll use real code snippets to show how to apply the recommended practices in an Android app.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "764351", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T15:50:00", + "endsAt": "2024-10-31T16:05:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b4cf78c9-623b-4b8f-b676-097fb7802668", - "name": "Manuel Vivo" - } - ], - "categories": [ + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" + }, { - "id": 53527, - "name": "Level", - "categoryItems": [ - { - "id": 185700, + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "d713390d-9628-412f-9778-9c45aeb6b13e", + "name": "Satish Shende" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "714221", + "title": "Jetpack Compose: Drawing without pain and recomposition", + "description": "This is a talk on recomposition in Jetpack Compose and the myths of too many calls it is followed by. I'll briefly explain the reasons behind recompositions and why they are not as problematic as they may seem. I have prepared numerous examples that illustrate how to minimize its occurrence.\r\n\r\nI'll share real-life situations we encountered during the redesign of our main screen. I'll delve step-by-step into how I optimized a particle animation without additional memory allocation and how we successfully reduced the number of recompositions on the screen. My practical guide on parameter tuning will be a great takeaway.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "34b0d49b-00dd-4dc1-ac1c-6de040e8a5b9", + "name": "Vitalii Markus" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "708032", + "title": "DIY Server-Driven UI: Building Auto Trader's SDUI platform", + "description": "Join us as we take you through Auto Trader’s 4-year journey in crafting our hybrid Server-Driven UI framework (Composable) and the architecture that powers our mobile apps. We’ll delve into why we decided to build instead of buy, our choice of Kotlin and the designs that fuelled our success. From pitfalls to breakthroughs, this talk will be full of useful insights for anyone considering building SDUI themselves.\r\n\r\nYou should leave this talk with the following:\r\n- Insights into our approach to SDUI\r\n- Building with a small team without stopping feature work.\r\n- Kotlin and Android tech insights of the system.\r\n- How we're leveraging KMP to improve client logic.\r\n- 4 years of learnings, did we make the right choice?\r\n- Tips and tricks for anyone considering the same.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "18ce6325-d3f4-4478-99d0-5683c4b924c2", + "name": "Jimmy Ray" + }, + { + "id": "0233c2ee-3bcd-4dee-9de5-c765b8c27cc0", + "name": "Harriet Taylor" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264381, + "name": "Cross-Platform" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "736123", + "title": "How we build Spotify for Android Automotive", + "description": "As the automotive industry embraces newer technologies and more mainstream software tech stacks, the in-car entertainment experience is rapidly evolving into a living room on wheels. In this session, we will take you on the journey of bringing Spotify to Android Automotive OS.\r\n\r\nIn this session, we will cover:\r\n\r\n* What is Android Automotive OS? Discover which car manufacturers and models are currently supported, and the different flavours of Android Automotive around!\r\n* Learn about the specific development challenges we faced while building Spotify for Android Automotive, including using Media Center templates to build features, adapting to diverse hardware, and challenging emulator setups.\r\n* Get insights into the unique aspects of releasing an app in the automotive ecosystem, from testing to ensure compliance with automotive standards to coordinating with car manufacturers for software updates.\r\n\r\nPlease join us to discover how we’re making the Spotify experience accessible, enjoyable and safe for drivers and passengers, and the innovative solutions we’ve developed to overcome the challenges of this cutting-edge platform. \r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "9c8384de-140e-47e4-aeb1-c85faf090fc8", + "name": "Danielle Vass" + }, + { + "id": "0aef86d4-a69b-4a82-bd8b-5ee05a3e421d", + "name": "Walid Tazout" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49272, + "room": "Things", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "732956", + "title": "Rewind and Resolve: A deep dive into building Session Replay for Android", + "description": "Understanding and debugging production issues can feel like solving a puzzle with missing pieces. We will dive deep into the journey of building a session replay feature for the Sentry Android SDK that helps developers capture those elusive bugs, respects user privacy, and maintains low overhead.\r\n\r\nIn this talk, we'll explore the technical aspects, including:\r\n\r\n- Taking screenshots of a Window\r\n- Redacting the screenshots to ensure no sensitive user data is leaked\r\n- Encoding the screenshots into a video\r\n- Collecting supporting data like breadcrumbs, network requests, logs, touches to improve debuggability\r\n- Maintaining low performance overhead while implementing all of the above.\r\n\r\nWe'll also demonstrate how our tool empowers Android engineers to gain actionable insights when solving errors, crashes, and ANRs.\r\n\r\nWhether you're an Android developer looking to improve your debugging toolkit or someone interested in the technical guts of the Android OS, the session will cover it all. Join us for a glimpse into the future of mobile debugging – it's a replay you won't want to miss!", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "da013a11-7cb0-4b0d-a37e-e29ce98688e0", + "name": "Roman Zavarnitsyn" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], + "sort": 4 + } + ], + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "736493", + "title": "Asynchrony and the infinite conveyor belt: Advanced coroutines and flows", + "description": "Most likely, you already know how to use coroutines and flows. They're part of our everyday existence. But all too often we learn them through repetition and rote learning, rather than forging a meaningful understanding of what's happening deep down. \r\nSo:\r\n\r\n- What *really* happens when you mark a function as 'suspend'?\r\n- How come crazy things like infinite loops on the main thread are possible?\r\n- What's the magic link between coroutines and flows?\r\n- What, really, is a scope? A context? A job?\r\n\r\nAnd there's a satisfying conclusion, too! We find that everything is linked: the path that seems to add complexity, in fact takes us back full circle.", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "1fb7ac5d-d848-4d40-840e-d8ae38128ab4", + "name": "Tom Colvin" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264362, + "name": "Flow" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264386, + "name": "Coroutines" + } + ], + "sort": 4 + } + ], + "roomId": 53982, + "room": "Nest", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "757517", + "title": "Roundtable: Community-Driven Development: How to Contribute to OSS", + "description": "Contributing to and maintaining open-source libraries in Android is a rewarding but challenging endeavor that benefits the developer community while enhancing personal skills. It involves code contributions, issue resolution, and ensuring the library remains stable and relevant over time. Join us as we have an open discussion about:\r\n•\tWhat motivated you to start contributing to or maintaining an open-source library in the Android ecosystem, or how do you get started?\r\n•\tWhat are the biggest challenges you face when maintaining an open-source project, particularly when managing contributions from other developers?\r\n•\tHow do you ensure that an open-source library remains compatible and up-to-date with new Android releases and API changes?\r\n•\tWhat are some best practices for encouraging meaningful contributions and fostering an active community around your open-source project?\r\n•\tHow do you balance the time commitment of maintaining an open-source library with other professional or personal responsibilities?\r\n", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "8730d40b-3fb7-450d-ab71-08c9dd81a48a", + "name": "Nicola Corti" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 303052, + "name": "Roundtable Discussion" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54703, + "room": "Chromecast", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764365", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:05:00", + "endsAt": "2024-10-31T16:45:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764370", + "title": "Ask Android! - WearOS, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-10-31T16:45:00", + "endsAt": "2024-10-31T17:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "771426", + "title": "#TheAndroidShow - Leadership Keynote", + "description": "Join members of the Android leadership team, including Matthew McCullough, Tor Norbye and Clara Bayarri for insights into what's new in Android", + "startsAt": "2024-10-31T17:00:00", + "endsAt": "2024-10-31T17:20:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264357, + "name": "Keynote" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "771431", + "title": "#TheAndroidShow - Panel Discussion", + "description": "Join moderator Florina Muntenescu and members of the Android Developer Relations team as they field questions on all the latest in Android development.", + "startsAt": "2024-10-31T17:30:00", + "endsAt": "2024-10-31T18:15:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "752bc716-f1b8-45d4-b0a5-cfbc3402b68b", + "name": "Florina Muntenescu" + }, + { + "id": "1932f921-2d6c-49e1-84b0-0d801cd2dbee", + "name": "Tor Norbye" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "e04a04d0-c875-4f36-ae02-914ba87b5b32", + "name": "Rebecca Gutteridge" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "4b613586-0a6e-45e9-b641-f855ceda4658", + "name": "Clara Bayarri" + }, + { + "id": "95ed3109-e7c3-41ff-a241-381c9fefb558", + "name": "Matthew McCullough" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 303052, + "name": "Roundtable Discussion" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "759067", + "title": "The Future of Android, Kotlin, and Everything", + "description": "Modern mobile and Android are a touch over 15 years old and Droidcon London 2024 is also in its 15th year. \r\n\r\nSo, it’s time to look forward.\r\n\r\nTech trends come and go. Some burn hot, then disappear. Some become essential. The web is essential. Mobile is essential. Whatever happens with mobile technology, the idea that mobile won’t be as, or more essential, 15 years from now is nonsense.\r\n\r\nHow mobile is developed and delivered is a different question altogether. This will be a talk about near-term tech possibilities, but not what mobile might look like in 15 years. I could speculate, and I'd be wrong. It will be a talk about how this community can shape that future, starting now.\r\n\r\nWhile the tech hype machine has hopped from one shiny concept to another, neither the web nor mobile has remained static. Far from it. We’re in a moment of change and opportunity. A very special moment for this community specifically. But the community is necessary to take advantage of it.", + "startsAt": "2024-11-01T09:20:00", + "endsAt": "2024-11-01T10:00:00", + "isServiceSession": false, + "isPlenumSession": true, + "speakers": [ + { + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264349, + "name": "Introductory and overview" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264357, + "name": "Keynote" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764372", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:00:00", + "endsAt": "2024-11-01T10:20:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "56b04326-604d-4140-b159-f93574c86ccd", + "name": "Ran Nachmany" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 306233, + "name": "Office Hours" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "733921", + "title": "Effective App Monitoring in Production", + "description": "Your feature is complete and ready to be shipped to production. Now you can forget about it completely and move onto the next thing, right? Actually the fun begins when your feature reaches your users. Is your feature healthy? Are your users seeing any issues? Which ones? How many?\r\nIn this session, we’ll discuss how to monitor Android apps in production to ensure they stand resilient and ready for the challenges of the real world. \r\n\r\nIn this session, you’ll learn how to design and report relevant events that matter to you and your team. If you are introducing monitoring in your application, you will learn how to design a tracking strategy to effectively monitor the application health and debug any issue users experience. If you already have monitoring in place, you will see techniques on how to get visibility into the data through the definition of KPIs (Key Performance Indicators) and dashboards. We will also provide examples of common problems teams face when implementing constant monitoring like knowledge sharing, alerting, and escalating issues to other teams.\r\n\r\nYou’ll see from real world examples how to track, detect and address production issues before they escalate.\r\n\r\nLevel up your app observability, delight your users and deliver with confidence with these tips.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "18c45d50-3e38-4933-a55b-8a833f850115", + "name": "Alejandra Stamato" + }, + { + "id": "084a1449-47c9-488c-b5b6-5b8534c45a23", + "name": "Mauro Frezza" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264377, + "name": "Modern Android Development" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49270, + "room": "Hangouts", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "696374", + "title": "AndroidX Lifecycle's Path to Multiplatform", + "description": "We’ve recently converted the AndroidX Lifecycle libraries (ViewModel, Lifecycle Runtime, and Compose support) to Kotlin multi-platform (KMP). Join this session to learn more about how this process went, what the real-world challenges of maintaining API backward compatibility are, what lessons we learned from working around KMP limitations, and insights to guide you in migrating your own libraries to KMP.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], + "sort": 4 + } + ], + "roomId": 49271, + "room": "Glass", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "696208", + "title": "Deep Dive into the Compose Compiler", + "description": "Have you ever wondered how the slot table is implemented in Compose? How are groups generated? What's the magic behind skipping, and how can you peek inside a slot table? The Slot table implementation was updated recently for performance. This talk takes a deep dive into the Jetpack Compose compiler internals, with a special focus on answering these questions and exploring the implementation details of the slot table. We'll go beyond the basics, uncovering how the compiler is structured and tested. This knowledge will empower you to understand, contribute to, or even build your own tools on top of it. Whether you're a curious developer or aspiring plugin contributor, get ready to fully understand the Jetpack Compose Compiler.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "a79f3682-df5c-40e4-8aa8-1b6eaa7bdeec", + "name": "Mohit Sarveiya" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264351, + "name": "Advanced" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264358, + "name": "Jetpack Compose" + }, + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264384, + "name": "Android" + } + ], + "sort": 4 + } + ], + "roomId": 49272, + "room": "Things", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "720892", + "title": "Seamless mobile real-time communication with WebRTC", + "description": "The WebRTC project allows developers to build smooth voice and video communication solutions by facilitating the transmission of data between peers.\r\nDuring this talk, we will explore the details of the WebRTC project, including its design principles, functionalities, and how it transforms communications for many web and mobile apps. \r\nFinally, we will create a simple mobile application in Kotlin Multiplatform using WebRTC so you will want to start a business and become a billionaire. \r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "4674e36f-50f8-4ffb-a486-679d730c759e", + "name": "Renaud Mathieu" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ + { + "id": 264354, + "name": "Session" + } + ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [ + { + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264364, + "name": "Compose" + }, + { + "id": 264388, + "name": "Libraries" + } + ], + "sort": 4 + } + ], + "roomId": 53981, + "room": "Stadia", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "731303", + "title": "Embracing 16 KB Page Sizes in Android - Boosting Performance", + "description": "Enhance app performance by adopting 16 KB page sizes introduced in Android 15, mandatory for Google Play uploads starting in 2025. In this session, I will cover the why and how behind 16 KB page size modifications through four parts: (1) Performance Boosts: Speed up app boot and launch times with 16 KB page size, (2) Impact Assessment: Evaluate how the 16 KB page size will affect your app, (3) Code Alignment: Align your code and binaries with the 16 KB page size requirement, and (4) Testing Strategies: Test and ensure your app is compliant with specific techniques. Get ahead of the curve by learning to adapt your app to the 16 KB page size requirement, ensuring superior performance and compliance ahead of the 2025 mandate.", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "84335047-a7cd-437e-986f-1d6b6df31072", + "name": "Chrystian Vieyra Cortes" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", + "categoryItems": [ + { + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529751", - "title": "Performance in Compose: Lessons learned from Lazy layouts", - "description": "The Compose team focused on performance extensively over the last few years. Many of the findings are documented and shared in different forms, but optimization continues to be one of the arcane arts of Compose. \r\n\r\nIn this talk, Andrei shares his own experience on optimizing Lazy layouts, focusing on the most notable tips that might help your application performance. He will provide a detailed description of Compose performance characteristics, summarizing a year long team effort to improve (re-)composition performance. Aside from the theory, you’ll also get practical tips about improving your composables derived from the experience of the Compose team.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "757520", + "title": "Roundtable: Keeping Up With Android – How to Stay Relevant", + "description": "\r\nIn the fast-paced world of Android development, staying current and continually honing your skills is essential for career growth and relevance. With constant advancements, new tools, libraries and APIs emerging…it can be easy to feel overwhelmed, have FOMO and feel like you are being left behind. In this roundtable we’ll discuss:\r\n - How do you prioritize what new technologies or trends to focus on?\r\n - What are some effective strategies or habits you use to continually hone your skills while balancing a busy work schedule?\r\n - How do you assess whether a new technology is worth investing time and energy into learning?\r\n - Can you share any resources, communities, or platforms that have been particularly helpful in staying up to date?\r\n - How do you prevent burnout?\r\n", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1e19e15d-18c9-4cd9-82b2-280120a8c8a6", - "name": "Andrei Shikov" + "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", + "name": "Neil Hutchison" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "527210", - "title": "Bluetooth Magic, first level", - "description": "We use it all the time, but what actually is Bluetooth and how do we add it to our apps? What is the magic behind it? \r\nJoin me in this talk to go over all the components required for assembling your own Bluetooth feature, how to discover devices, how to transfer data among them and what different options do we have. All of this grounded with examples, tips and tricks coming from my own experience with it.\r\nAfter this talk, you should have the base knowledge to add Bluetooth to your apps. Because, sometimes, we all, need a little bit of... magic!", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "764376", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T10:20:00", + "endsAt": "2024-11-01T11:00:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94ba4fa4-aed7-482e-8096-f61090d5bb4d", - "name": "Nicole Terc" + "id": "e2ad2515-5292-4deb-8ac6-6c7ea5be1543", + "name": "Rob Orgiu" + }, + { + "id": "f55e8e03-a341-4c8a-b4fc-a7e97f82db07", + "name": "Xiaodao Wu" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "8b706c47-edbc-41e3-af85-3328b4134e57", + "name": "Ben Sagmoe" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "d7f1c9d9-d537-4240-864e-ba626457f534", + "name": "Satish Shende" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764378", + "title": "Ask Android! - Gen AI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:00:00", + "endsAt": "2024-11-01T11:15:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "391191b2-1d5c-4784-875b-00edf4d065cc", + "name": "Chris Assigbe" + }, + { + "id": "c83dc04e-7a0a-4cbc-9d73-10d08f18a68b", + "name": "Miguel Montemayor" + }, + { + "id": "c70d10a5-999c-4858-a39c-7326570cee91", + "name": "Ran Nachmany" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185719, - "name": "Other" + "id": 306233, + "name": "Office Hours" } ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "520431", - "title": "Mobile Observability at scale", - "description": "Mobile applications have evolved in complexity and business significance, making it critical to closely monitor and maintain the apps availability and performance. Fast incident understanding and response in real time is crucial to keep trust both of your users and your stakeholders. \r\n\r\nIn this talk we will introduce Mobile Observability, explaining its basics and highlighting both commonalities and distinction with backend practices. Our focus will be on various metrics that can be collected within mobile apps, their purpose, instrumentation methods and alignment with Android Vitals or backend metrics.\r\n\r\nAndroid engineers will learn \r\n- How to set up Observability within their apps, establishing actionable metrics.\r\n- Site Reliability Engineering (SRE) practices and their application in the mobile world.\r\n- Selecting right tools and configuring alerting systems for appropriate response and actions.\r\n- Scaling Observability across 10+ teams.\r\nMaintenance, handling seasonality, legacy app versions or false positives.\r\n- Incident response best practices and techniques for quickly identifying the cause of issues.\r\n", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "736477", + "title": "Creating a Custom Compose Layout, Step-by-Step", + "description": "Building a custom layout in Jetpack Compose may seem intimidating, but it does not have to be. With an understanding of the tools available for custom layout, the phases of composition, and best practices for state management, anyone can create a powerful, beautiful custom layout.\r\n\r\nIn this talk, we will build a sophisticated schedule component step-by-step. This composable will include multiple display modes, theme-like encapsulation of styling and behaviors, scoped modifiers for easy customization, and even some animation.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "3851a67a-7332-4247-a6cb-4e87efbf256c", - "name": "Josef Raska" + "id": "2d41d3fb-3321-4b47-a401-ae84a5de2423", + "name": "Huyen Tue Dao" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" + "id": 264358, + "name": "Jetpack Compose" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530249", - "title": "Creating Engaging Android TV and Fire TV Apps with Native and Cross-Platform Tools", - "description": "Ready to launch your app or game on the big screen? Whether you are targeting Google Android TV or Amazon Fire TV, this talk is your guide to creating great “10 foot UIs” for customers watching television on their couch!\r\nIn this session, we’ll dive into the best practices for TV app development. Get ready to explore the key design principles, development strategies, and performance optimizations. We’ll cover everything you need to know, from creating beautiful UIs with Kotlin and Jetpack Compose for TV, React Native or Flutter, to optimizing remote control inputs to ensure a seamless navigation experience.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "747072", + "title": "Scalable Testing Strategies", + "description": "It's very easy to have too many tests and lose track of what kind of feedback each one brings. Moreover, testing on multiple devices can get expensive, both in terms of CI cost and lost productivity waiting for tests to pass. In this talk we'll talk about how the testing pyramid has evolved because of new requirements and features that need to be tested, such as multiple form factors, new configuration changes, Jetpack Compose, screenshot testing, etc.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "106dc281-fe8b-40ac-b16b-240832b27b47", - "name": "Giovanni Laquidara" + "id": "578f244d-fe79-4759-a84f-63dfbefbd06d", + "name": "Jose Alcérreca" + }, + { + "id": "532dc16d-b69d-4f68-aa49-fe9878b0b52f", + "name": "Adarsh Fernando" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264363, + "name": "Testing" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185728, - "name": "Modern Android Development" + "id": 264394, + "name": "CI/CD" }, { - "id": 185736, - "name": "Design" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "518711", - "title": "Hitting a Gold Mine: How to Set the Gold Standard With Golden Tests", - "description": "Golden tests generate screenshots of Flutter widgets and compare them against reference images. They are a powerful tool to protect from visual regressions and improve the quality of your apps. \r\n\r\nIn this talk, we’ll go beyond simple examples and take a look at how we can make golden tests part of a development workflow in a bigger Flutter project. In a Flutter project developed by LeanCode for a client from the banking sector, we decided to experiment with golden tests from the very beginning, and they turned out to be extremely effective. \r\n\r\nDuring the talk, we’ll see what are some good practices and antipatterns for golden tests, what role they can play in the code review process, and take a broader perspective on who can be involved in golden tests (not only developers!). We’ll also consider using golden tests in more advanced testing scenarios for things like responsiveness, accessibility, localization, and others.", - "startsAt": "2023-10-27T10:15:00", - "endsAt": "2023-10-27T10:55:00", + "id": "735816", + "title": "Developing Trusted Applications for Trusted Execution Environments and vTEEs", + "description": "In today's mobile-first world, security is paramount. Join us in this engaging session where we introduce vTEE, a groundbreaking concept designed to embed advanced security into mobile applications. This session will demonstrate how vTEE allows developers to create their own Trusted Execution Environments (TEEs) directly within their apps, ensuring that sensitive data remains protected. Additionally, learn how our global award-winning open-source project, jCardSim, simplifies the prototyping and development of trusted applications, accelerating your journey towards secure, scalable, and innovative app experiences. Get ready to supercharge your app security with ease and flexibility!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "15d3a177-f21f-4b7b-8f42-3f7694477f21", - "name": "Marcin Chudy" + "id": "d7e7d278-a645-42c5-b337-c793a5b5bd79", + "name": "Mikhail Dudarev" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264372, + "name": "Security" }, { - "id": 185727, - "name": "Flutter" + "id": 264381, + "name": "Cross-Platform" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538655", - "title": "Practical Magic with Animations in Compose", - "description": "Are you constantly in awe of the animations your designer creates but have no idea how to implement them? Or maybe you want to keep your users engaged by adding delightful little treats in your app... There are a few key principles that unlock a wide range of different animations, from basic to advanced.\r\n\r\nWe will go through some practical examples of how to implement animations in Jetpack Compose, such as working with gestures to control animations and how to do state based animations. Join us to learn how to think about implementing any animation in a step-by-step way. ", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "722874", + "title": "From realtime CameraX filters to General Purpose GPU computing in simple steps", + "description": "Did you ever think that that the computational complexity of image processing is comparable to that of AI training, and both tasks use GPUs?\r\n\r\nModern phones have powerful GPUs that are typically used for photo, graphics and image processing which naturally brings us to OpenGL and Vulkan C++ APIs.\r\n\r\nLuckily, on Android we have OpenGL Java bindings, so we don't need to dive into C++. And combined with the CameraX effects API, we can build advanced filters for the camera. What is more, mobile GPUs are also not limited to these types of tasks and can be used for general purpose computing, even the ones completely unrelated to graphics.\r\n\r\nIn this session I will show you how to set up and write your own shaders for real-time image processing with CameraX. I will also show you how to make a step forward and use shaders for general purpose GPU computing, so that you can take advantage of the GPU power.\r\n\r\nCome to this talk and learn how to harness the computing power of your phone's GPU!", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", - "name": "Rebecca Franks" + "id": "892ec2dc-5b2e-45fb-9393-ff1407ebebe7", + "name": "Konstantin Zolotov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [], + "categoryItems": [ + { + "id": 264369, + "name": "Other" + }, + { + "id": 264384, + "name": "Android" + }, + { + "id": 264388, + "name": "Libraries" + } + ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "520549", - "title": "Composing an API the *right* way", - "description": "Everyone who writes code using Jetpack Compose designs Composable functions and components all the time. In this talk, we'll take a look at some highlights from the official guidelines around designing Compose APIs, to see how we can do a better job of building with Compose.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "711918", + "title": "Personalizing Accessibility", + "description": "What should you do when users' accessibility needs conflict? For example, some users prefer more color contrast, while others require less contrast. To give a few examples, some might need labels to accompany icons, and for some, having too much text can make the app experience worse. Some love graphs and others have difficulty understanding what they represent and would need the same data in written format.\r\n\r\nOne way to tackle this problem is to allow users to personalize their app experience. In this talk, I will discuss different ways of personalization, including the opportunities and drawbacks. I will also give some actionable examples of how to let your users personalize their experience. After listening to this talk, you'll leave with actionable knowledge about providing a more accessible experience for your users via personalization.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "1916ae1c-96d7-480f-9eed-bf5dce444441", - "name": "Márton Braun" + "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", + "name": "Eeva-Jonna Panula" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, - "name": "Kotlin" + "id": 264365, + "name": "Accessibility" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529068", - "title": "Demystifying Dagger", - "description": "Dagger gets a bad rap for being \"difficult to understand\", or \"overly complicated\", or \"black magic\". In reality, Dagger generates pretty straight-forward, elegant wiring code similar to how we would likely write it manually and, I believe, it's the complexity of large scale apps that makes it complex.\r\n\r\nIn this talk we'll look at different types of injection that Dagger supports, and the code it generates to do so, to get an understanding of what it's doing and how it works. We'll then dive into Components and Subcomponents to see how Dagger wires together our dependencies into a graph. Finally, we'll look at how Dagger can be extended using Anvil and other tools.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "760569", + "title": "Roundtable: KMP & CMP - Yes we can!...but....", + "description": "KMP with native UI and KMP with CMP are both amazing technically. However, there is no guidance for how these technologies should be applied to production situations. All new tech goes through a maturing phase.\r\n\r\nJoin this roundtable to discuss the promise and pitfalls of KMP and CMP and how the community can deliver on the amazing potential.", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f1275dc9-492b-4ab1-b31b-3e19bccde628", - "name": "Ryan Harter" + "id": "84a7e91b-e090-4d57-9eed-f3b9f277dc95", + "name": "Kevin Galligan" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 303052, + "name": "Roundtable Discussion" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185738, - "name": "Dagger" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 54703, + "room": "Chromecast", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "536951", - "title": "Android Large Screen: how your app can power a workstation", - "description": "Learn how to make your Android Apps Large Screen Ready and Optimized.\r\n\r\nUnderstand what code changes are needed to get Android, Web and Flutter apps working well on desktop-style, multi-screen, or multi-window environments.\r\n\r\nApps that have been coded with accessibility (a11y) in mind from the beginning have a head-start in being Large Screen Optimized. Learn why a11y may be needed by anyone in certain situations, how it extends the reach of your apps, and how to bake-in accessible user experiences from the start.\r\n\r\nSee how Zebra Workstation Connect enables a Zebra mobile device to become a workstation/Point of Sale by utilising the Android Large Screen capabilities.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "764381", + "title": "Ask Android! -GenAI, Media, Adaptive apps on all form factors", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:15:00", + "endsAt": "2024-11-01T11:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ca54f2c6-836e-41b2-ba24-78870a7c0384", - "name": "Benedict Kennedy" + "id": "c8229c07-b474-4044-af66-1c8da6779bc6", + "name": "Chiko Shimizu" + }, + { + "id": "f89ea766-4af9-4b88-8842-ede574e4c648", + "name": "Paul Lammertsma" + }, + { + "id": "3b5772d2-d3d5-42fe-8015-8f7b0e09b472", + "name": "Thomas Weathers" + }, + { + "id": "837dd36a-5782-49cc-8da5-3f7fc7e4da8a", + "name": "Kateryna Semenova" + }, + { + "id": "ef1c33eb-89e9-49f6-9abc-da8140988097", + "name": "Alex Vanyo" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185714, - "name": "Accessibility" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185736, - "name": "Design" - }, - { - "id": 185741, - "name": "UI/UX" - }, - { - "id": 185744, - "name": "sponsor" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530186", - "title": "Coding for Good: Achieving social change with an app", - "description": "As coders, we have an enviable ability to create platforms and apps that bring people together. The world is not short of challenges right now, especially climate change, but we will only have a real impact on the world if we can help people unite and organise to pressure those with power and influence.\r\n\r\nChris and his husband quickly developed an app ten years ago to allow ordinary people to lobby the normally inaccessible and unelected House of Lords to pass the Same Sex Marriage Bill. It unexpectedly took off, with 15,000 emails sent in the space of two weeks, helping the Bill overwhelmingly succeed in the normally-socially-conservative upper House of Parliament in the UK.\r\n\r\nHe'll talk here about how he did it, and how you - an individual with a computer and knowledge of how to knock an app together - can use this skill to give a voice to the voiceless, and to potentially change or even save the world.", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "764391", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T11:55:00", + "endsAt": "2024-11-01T12:55:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "f53882b1-df66-4d6c-9ec1-888badcdd224", - "name": "Chris Ward" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "518103", - "title": "Nested Navigation in flutter web", - "description": "In Flutter web, developing Nested navigation is always a critical topic. \r\nEvery project has different needs, Some project needs Bottom navigation some need Sidebar navigation, and some need some sidebar with nested navigation. Too many combinations and complications!! it’s a hard decision to decide on an approach similar to the packages. Which package should we use and why? In this talk, I am sharing my story about Go Router. How Shell route is a change maker and solved many problems. Let’s learn more about the Shell route. and its different use cases. Make Flutter web navigation simple and easy.\r\n", - "startsAt": "2023-10-27T11:10:00", - "endsAt": "2023-10-27T11:50:00", + "id": "700387", + "title": "Mistakes you make using Kotlin Coroutines", + "description": "Kotlin Coroutines offer a powerful, yet deceptively simple solution for managing asynchronous tasks. However, their ease of use can sometimes lead us into unforeseen pitfalls. From unexpected cancellations to perpetually running operations, developers often encounter bewildering behaviors that can affect application performance and reliability. In this presentation, we'll dive deep into the common mistakes made while using Kotlin Coroutines. We'll explore why these issues arise, how to diagnose them, and, most importantly, how to resolve them effectively. Expect to walk away with advanced insights and practical strategies to harness the full potential of coroutines in your Kotlin applications, ensuring they're robust, efficient, and maintainable.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "990dea5b-f32e-4260-8551-967e2597bee2", - "name": "Renuka Kelkar" + "id": "ef7c1115-d852-44b9-9f72-530ba7223c55", + "name": "Marcin Moskala" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185727, - "name": "Flutter" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264386, + "name": "Coroutines" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "513549", - "title": "Blast Off: Managing Hundreds of UI Updates for Emoji Cannons", - "description": "Managing a state might be a challenge. Managing the state with hundreds of updates and constant recomposition of floating emojis is a challenge indeed. In this talk, I will share how to build emoji cannon that floods your screen with UI elements, update the state, and does not freeze your UI. All of that with Jetpack Compose :) \r\nIn addition to covering the technical aspects of building and managing a state, I will also share how to build a responsive UI that takes the user input and animates.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "736513", + "title": "A Snapshot Preview of Paparazzi 2.0", + "description": "Since last Droidcon NYC, Paparazzi has seen continued increased adoption across the Android community!\r\n\r\nIn this session, we'll discuss:\r\n* why pixel perfection is challenging af\r\n* image encoding and why we chose APNG for animations\r\n* why Google's publishing of layoutlib is great for the community\r\n* ...and more!\r\n\r\nWe'll also give a sneak peek to what's coming in 2.0, as well as why we're excited about it and you should be too!", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b6dc4b56-ca5d-4e55-8a57-e37f6e6d6af0", - "name": "Piotr Prus" + "id": "903c7a65-63f0-4244-8f8c-7ab8f7acae75", + "name": "John Rodriguez" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264363, + "name": "Testing" + }, + { + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "502207", - "title": "Take your shot of Vitamin!", - "description": "Decathlon has more than 160 frontend products, including 50 dedicated to mobile applications. Due to this context, it is hard to align the user interface across all these projects while respecting the platform.\r\n\r\nVitamin is a Design System developed by Decathlon as a product which can be adapted to any context and with multiple technical implementations for Android, iOS and Web. In theory, you can use this Design System in your application and customize it to fit your theme and your needs.\r\n\r\nIn this presentation, I'll focus on Vitamin Compose, the design and technical architecture, biases and what are the next steps.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "731650", + "title": "Elevated Dependency Injection: Going Beyond the Basics with Custom Hilt Components", + "description": "Hilt/Dagger is a popular and powerful building block in Modern Android Development. Its benefits include reduced boilerplate and compile-time magic to ensure runtime safety and performance. \r\n\r\nBy default, Hilt provides basic components, such as Singleton, Activity, or ViewModel. \r\nIt is also extensible, allowing you to create additional components for more complex use cases. \r\n\r\nHere at Patreon, building a custom UserComponent tied to a user session has improved our codebase immensely by reducing boilerplate, increasing code safety, and making it easier to cancel work when a user logs out.\r\n\r\nThis talk will focus on the ins and outs of custom components and some simple but non-obvious techniques that we’ve used to make them nicely integrate into our app!\r\n\r\nHighlights of the talk will include:\r\n\r\n- Custom components and when you should use them\r\n- The beautiful synergy of dagger components and coroutine scopes\r\n- Overcoming the limitations of Dagger’s single inheritance and Hilt’s fixed component hierarchy\r\n- Providing easy access to bindings in `@HiltViewModel` and `@HiltAndroidTest` classes\r\n- A case study of Patreon’s `UserComponent`", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", - "name": "Gerard Paligot" + "id": "1caff1f9-9594-4ce8-a031-ed1ba2201d13", + "name": "Steven Kideckel" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185736, - "name": "Design" + "id": 264384, + "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264387, + "name": "Dagger" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495165", - "title": "How to handle incidents at scale", - "description": "In this session, I want to guide the audience into applying healthy processes to handle incidents. We have all probably been in the situation of having our apps unexpectedly crashing - either because of a bad release, because of unexpected Backend changes, etc.\r\n\r\nIn these scenarios, we usually get overwhelmed by messages from different departments (support folks, managers, and so on), this is especially true when operating at a large scale, and things can quickly get out of control.\r\n\r\nThis talk aims to propose rules that will not only mitigate the above scenarios but also improve the communication flow, give tips about how to set up alerting systems and what to measure, and talk about a \"post-mortem\" process.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "700492", + "title": "Ten things you heard about testing that might be wrong", + "description": "Testing became an essential part of Android development. Many conference talks have been given and even more best practices have been written. \r\n\r\nBut what if, as time evolved, some of the things we thought were true, changed?\r\n\r\nLet’s start questioning some of these in this talk:\r\n- Are flaky tests fixable?\r\n- Are mocks even harmful?\r\n- Is DI about testing?\r\n- Did we understand testing in isolation properly? \r\n- Is the test pyramid still valid?\r\n- Is Robolectric good or bad on Android?\r\n- And in times of AI, should we generate tests?\r\n\r\nCome and join my session to learn more!\r\n", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "ac9cef8a-3072-4e52-8064-a5155be542b0", - "name": "Alessandro Mautone" + "id": "4d3a31b1-61e5-4a26-9ca3-c9f30e3eaa08", + "name": "Danny Preussler" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185719, - "name": "Other" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528097", - "title": "Beyond the Basics: Performance Monitoring and User Experience for Mobile App Growth", - "description": "When interacting with a mobile app that regularly crashes or freezes, 53% of users uninstalled the app, 37% stopped using it, and 28% looked for a replacement. Users are no longer forgiving mobile app mishaps and errors. Going beyond the basic metrics of understanding your user experience and diving deep into mobile performance is key to ensuring growth and positive user experiences. We will highlight which metrics and takeaways mobile teams should track to create a superior app experience that takes into account every user interaction. Find out why app insights, proactive issue detection, advanced debugging, and alert management capabilities should matter to you and the development of your mobile app.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "735538", + "title": "Unblocking The Main Thread: Solving ANRs and Frozen Frames", + "description": "In the realm of Android development, the main thread is our stage, but too often, it becomes a battleground where performance issues arise, leading to ANRs, frozen frames, and sluggish Uls. As we strive for excellence in user experience, understanding and optimizing the main thread becomes essential to prevent these common performance bottlenecks.\r\n\r\nWe have strategies and best practices for keeping the main thread uncluttered. We'll examine the root causes of performance issues and techniques for monitoring and improving main thread health as well as app performance.\r\n\r\nIn this talk, participants will walk away with practical knowledge on enhancing app performance by mastering the main thread. We'll share proven approaches to eliminate real-life ANRs and frozen frames to build apps that deliver butter smooth experience.", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6519e3d1-ca80-46b3-99f5-0f77adfdc91f", - "name": "Sean Higgins" + "id": "9a7f5add-ce7a-49a3-ab3d-f3c4484453e8", + "name": "Sinan Kozak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185712, - "name": "Testing" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185727, - "name": "Flutter" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185731, + "id": 264380, "name": "Tooling" }, { - "id": 185732, - "name": "Cross-Platform" - }, - { - "id": 185735, + "id": 264384, "name": "Android" }, { - "id": 185741, - "name": "UI/UX" + "id": 264379, + "name": "Android Studio" }, { - "id": 185742, + "id": 264391, "name": "Firebase" - }, - { - "id": 185744, - "name": "sponsor" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "511354", - "title": "Refactoring and Test Fakes: Crafting Resilient Code with Confidence", - "description": "Crafting resilient code is one of the most important things we do as software developers, but it's much easier said than done! Building with confidence requires an appropriate test harness and automated safeguards to ensure your software is robust.\r\n\r\nIn most real world scenarios, we don't have the luxury of working with a green field project, so it can be difficult to apply best practices whilst maintaining legacy code. How then can we refactor, and effectively utilise test fakes appropriately?\r\n\r\nIn this talk, I'll discuss the best approaches for using test fakes, mocks, stubs, and what are the pros and cons for each. How we can avoid writing brittle tests, slowing down development, and build scalable apps that can stand the test of time.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "764396", + "title": "Ask Android! - Compose, Media, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T12:55:00", + "endsAt": "2024-11-01T13:35:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", - "name": "Ash Davies" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "e34e8c09-ac65-4927-852e-ff2fc462afac", + "name": "Anton Beloglazov" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "1c163a41-865b-4915-b032-ef4942e2f47c", + "name": "Kristina Simakova" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764397", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:35:00", + "endsAt": "2024-11-01T13:50:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185716, - "name": "Modularization" + "id": 306233, + "name": "Office Hours" } ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "521242", - "title": "How not to ship an app. Lessons and experiences shipping bugs to production.", - "description": "Have you ever experienced the anxiety of shipping a mobile app to production, with your fingers crossed that nothing is going to go wrong? If so then this is the talk for you, and if it is not, maybe you will learn something new.\r\n\r\nOver the past 2+ years, we have shipped a lot of bugs in our production app, with this talk I aim to condense the most important learnings we had and the actions we took to mitigate those. Openly sharing our experiences will not only help you avoid similar pitfalls but also foster a culture of learning and improvement within the Flutter community.\r\n\r\nWhether you are an aspiring developer, an experienced professional, or a team lead, this talk will offer valuable insights and practical tips to improve your app's quality.", - "startsAt": "2023-10-27T12:05:00", - "endsAt": "2023-10-27T12:25:00", + "id": "733770", + "title": "User Initiated Data Transfer Jobs", + "description": "Google introduced a new api UIDT in Android 14 as an alternative to Work Manager and Foreground Services.\r\n\r\nA deep dive session into how your apps can run longer-duration, user-initiated transferring of data, such as downloading a file from a remote server using UIDT. With increasing restrictions on running Foreground Services it is very important to migrate your app's critical use cases to alternate API's. Android 14 applies strict rules on when you can run a Foreground Service & Android 15 is bringing 6 hours timeout to Foreground Services.\r\n\r\nWill share how specific use cases migrated to UIDT can lead to improved app performance.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "758dff56-ba98-4d4d-ab60-87472b2b4846", - "name": "Efthymios Sarmpanis" + "id": "edd46ee9-3e55-478c-8f27-c8f86ca77138", + "name": "Tushar Varshney" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185703, - "name": "Lightning talk" + "id": 264354, + "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185732, - "name": "Cross-Platform" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538652", - "title": "Why is my app letterboxed?!", - "description": "Have you ever wondered why your app is running in a small window in the middle of the screen, with bars on the sides? This usually happens on larger screens, but why?\r\n\r\nThis is called letterboxing, which is an Android app compatibility mode the platform places an app in if it cannot support a certain window size. In this talk, we will explore why letterboxing happens and how to optimize your app to avoid it. We will also discuss how to ensure that your app looks good on all screens, regardless of the screen orientation or aspect ratio.\r\n\r\nBy the end of this talk, you will be able to:\r\n- Identify the causes of apps running in letterbox mode.\r\n- Understand how to fix this issue.\r\n- Ensure that your app looks good on all devices.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734509", + "title": "Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components", + "description": "Are your ViewModels exponentially growing out of control as they manage the state for each of your Composables? This talk introduces Molecule, a new library for creating state holders in Jetpack Compose. We’ll explore how Molecule can simplify presentation state management in Compose, allowing for more flexible and scalable UI development. Attendees will learn how to implement Molecule in their projects and understand its advantages over traditional state management approaches as well as some of the drawbacks to this approach.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7d912e80-f02c-42cf-92db-98e670bef1a6", - "name": "Roberto Orgiu" + "id": "6624cb6c-7fc4-4495-a87f-8060e88d7d18", + "name": "Jack Adams" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185735, - "name": "Android" + "id": 264362, + "name": "Flow" }, { - "id": 185737, - "name": "Coroutines" + "id": 264364, + "name": "Compose" + }, + { + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185739, + "id": 264388, "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495137", - "title": "Practical ADB usage to enhance your life!", - "description": "ADB is an incredibly underutilized tool that can dramatically improve your Android development experience! Maybe you know some basic ADB commands, perhaps even a couple of nifty advanced ones or possibly, you don’t know what ADB even is!\r\n\r\nImagine automatically & swiftly logging into your app’s test account, instantly inspecting your app’s current Activity/Fragment stack or editing your app’s shared preferences & phone settings all without touching your device! All this and SO MUCH MORE is possible thanks to ADB.\r\n\r\nThis session is not intended to blast you with a list of ADB commands but instead to share a collection of practical scenarios & examples how you can use this supreme tool everyday in a variety of ACTUALLY useful ways. \r\n\r\nJoin me in this session, regardless of your experience, as we explore what this tool can really do to change the way you work forever!", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "736447", + "title": "You don't have to run it locally! How to run your emulators in the cloud.", + "description": "Probably every Android engineer has tried running Android Emulator on their laptops. Many companies have experience running Android Emulators in CI pipelines for testing. But what does it take to run a highly interactive emulator in the cloud? Why would one need to run an emulator in the cloud?\r\n\r\nAt Uber we know how to build Cloud Development Environments. And now we expanded into Android Emulator space. Providing our engineers with zero setup emulators. \r\n\r\nWe'll walk you through all the moving parts of the emulator setup in a dev environment that sparks joy. Share the challenges we had and opportunities we discovered.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", - "name": "Benjamin Kadel" + "id": "695e56a6-62a8-496b-9082-8cc4dadec337", + "name": "Petras Vičiūnas" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185731, + "id": 264380, "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495999", - "title": "A Picture Is Worth A 1,000 ... Lines? Devs?: Get the full picture (on image loading) with Coil", - "description": "To download an image on Android, all you need are these lines of code:\r\n val input = URL(“https://cutecatsimage”).openStream()\r\n val bitmap = BitmapFactory.decodeStream(input)\r\n imageView.setImageBitmap(bitmap)\r\nWhy then, are there whole projects and teams dedicated to doing this on Android like Coil, Picasso, Glide, etc. In this talk we’ll focus on Coil and look under the hood to break down the inner workings of Coil’s image downloading pipeline. We’ll go way beyond using `imageViewload(“https://cutecatsimage”)`, and dive deep into Coil’s source code to understand how it handles loading your image into memory, caching, and transforming your image. You’ll walk away from this talk with a rich understanding of the journey the images you’re working with take to show up on your screen, and a being in a much better spot to work with problems that could arise.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "734664", + "title": "Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.", + "description": "With the advent of Android 15, edge-to-edge design has become the default configuration. Consequently, applications must be capable of accommodating window insets, including the system status bar and navigation bar, as well as supporting drawing under display cutouts and other system UI elements. This presentation will address the most prevalent challenges encountered when supporting such a diverse range of UI configurations. Additionally, it will illustrate how to utilize Android previews and testing with Jetpack Compose to effectively navigate this complexity and guarantee an optimal user experience across all scenarios.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "a13934a7-6137-4a94-9a03-84919fce12f4", - "name": "Rafa Moreno" + "id": "257fcb0c-cc84-4892-a1f1-54985071e0a6", + "name": "Timo Drick" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" + "id": 264358, + "name": "Jetpack Compose" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264363, + "name": "Testing" }, { - "id": 185735, + "id": 264384, "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529953", - "title": "Why can't my app open that file? A deep dive into the Android app sandbox", - "description": "Android keeps a close eye on what files your application can read and write. We call this the \"application sandbox\": a safe area for your code to run which prevents other apps from interfering with your data, and prevents you from interfering with other apps or the operating system\r\n\r\nIn this talk I will be looking at the rationale behind the sandbox, and how it works. I will look at file ownership, file access modes and the reasons for those \"Permission denied\" messages. I will dig down to find out exactly why an app can access only a well defined set of private files and shared storage. And I will explain\r\nhow SE Linux enforces all of this at at a deep level\r\n\r\nYou will be reassured that Android is a secure operating system. But, as you know there are always exceptions to the rules. So, in the final section of this talk I will show you how preinstalled system apps can crash though all the barriers\r\n", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "704275", + "title": "The state of code coverage for Kotlin", + "description": "Nowadays it’s not anymore a question whether we want to write tests or not. Writing unit tests is not “nice-to-have” it’s an essential part of our everyday job. While that’s already a great step forward, how can we be sure our tests are actually testing something. To check this we all use code coverage tools.\r\n\r\nIn this presentation you will learn how code coverage for Kotlin works, which tools we can use and what are the challenges and limitations of various approaches. You will learn the difference between line, instruction and condition coverage and how to read and interpret coverage results in Kotlin.\r\n\r\nIn the last part of this presentation we will cover the future for Kotlin Code Coverage, what is missing and what can be improved in the future.", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "5d3a7bdc-4d73-4985-91f2-a388978732a9", - "name": "Chris Simmonds" + "id": "eeb25beb-5d37-4fb7-acac-170357a1d89b", + "name": "Marharyta Nedzelska" + }, + { + "id": "d2ddc9e3-35b6-44a9-8daa-144d89cabaff", + "name": "Evgeny Mandrikov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185722, - "name": "Security" - }, - { - "id": 185724, - "name": "API" - }, - { - "id": 185735, - "name": "Android" + "id": 264359, + "name": "Kotlin" }, { - "id": 185736, - "name": "Design" + "id": 264363, + "name": "Testing" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "496982", - "title": "Improving Video Playback with ExoPlayer", - "description": "Video has become an integral part of our life, and we are witnessing a significant rise in the integration of video content within Android apps. Sadly, there isn't much information out there about videos on Android. I've personally had a hard time finding practical ways to make videos better.\r\n\r\nIn this talk, my primary focus will be on sharing practical approaches with ExoPlayer that go beyond what is documented: We'll discuss the common problems with playbacks, solutions and will find a performant approach to use ExoPlayer together with Jetpack Compose.\r\n\r\nI'll share everything that I learned practically during the last 8 months:\r\n- Video basics: Media3, ExoPlayer and how it works\r\n- Bandwidth and it's role\r\n- Adaptive streamable protocols (HLS/Dash) vs custom ABR implementation\r\n- Caching and Video Prefetching\r\n- Initial video resolution improvements\r\n- Time to the first frame improvements\r\n- Performance with Jetpack Compose\r\n- Architectural Approaches\r\n\r\nEverything that will be mentioned is validated through real production scenarios and confirmed an efficiency by A/B tests. ", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "764398", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T13:50:00", + "endsAt": "2024-11-01T14:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "61cc0eba-ac00-414b-9cf7-5525040a4bdd", - "name": "Alexey Bykov" + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "e4292032-5462-494b-abfd-3898301476e2", + "name": "Rebecca Franks" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", + "categoryItems": [], + "sort": 4 + } + ], + "roomId": 54706, + "room": "Ask Android", + "liveUrl": null, + "recordingUrl": null, + "status": "Accepted", + "isInformed": true, + "isConfirmed": true + }, + { + "questionAnswers": [], + "id": "764399", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:30:00", + "endsAt": "2024-11-01T14:50:00", + "isServiceSession": false, + "isPlenumSession": false, + "speakers": [ + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "1dfe9bb5-d9bf-41a9-b7d6-dcfd7953e941", + "name": "Nick Butcher" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "c16c95a9-0e07-4838-9cba-5d041aee4d14", + "name": "Rebecca Franks" + } + ], + "categories": [ + { + "id": 74391, + "name": "Level", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - }, + "id": 264350, + "name": "Intermediate" + } + ], + "sort": 0 + }, + { + "id": 74392, + "name": "Session format", + "categoryItems": [ { - "id": 185739, - "name": "Libraries" + "id": 306233, + "name": "Office Hours" } ], + "sort": 1 + }, + { + "id": 74393, + "name": "Tags", + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530144", - "title": "Deep Dive into Flutter Animations: Crafting Dynamic and Engaging UIs", - "description": "Animations are more than just visual flair; they breathe life into mobile applications, making them feel responsive and intuitive. In this session, we'll embark on a deep dive into Flutter's animation toolkit. From the foundational principles of animating widgets to advanced techniques like gesture-driven transitions and physics-based movements, attendees will discover how to elevate their app's user experience to new heights. Through hands-on demonstrations, learn to harness the full potential of Flutter's animation capabilities and craft truly dynamic and engaging UIs.", - "startsAt": "2023-10-27T13:25:00", - "endsAt": "2023-10-27T14:05:00", + "id": "693397", + "title": "Crime Scene InvestiGITor", + "description": "THERE HAS BEEN A MURDER!\r\n...\r\n(Or whatever the code version of a murder is... like breaking unit tests, a bug maybe?... I dunno!)\r\n\r\nTogether, we will learn how to become a professional and revered investiGITor, who will be able to sniff out and solve any version control offence with the ease and panache of a seasoned detective!\r\n\r\nVersion control software is often a mysterious black-box that we HAVE TO interact with in order to successfully collaborate with others. But what if it doesn't need to be a confusing & complicated enigma?\r\n\r\nYou will learn to probe into the dark recesses of Git and understand its inner workings by learning how to carry out many tasks that you will undoubtedly need to perform at some point in your career.\r\n\r\nFor example:\r\n* Ever needed to safely remove a secret that you accidentally stored in the repository?\r\n* Ever had your app break, not know why, and then needed to quickly hunt down the exact commit where a bug was introduced?\r\n* Ever needed to travel back in time, through history, to stop a crime before it even happens...(sort of)?\r\n\r\n...All these things and a bunch more useful & interesting, lesser-known ways to become a masterful git detective and truly understand the most important tool in a developer's arsenal.\r\n\r\nSo come have a little fun with me in this talk, bring the bugs to justice, solve the case of the naughty commit & become a hero by defending the integrity of your codebase!", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6284f70b-bae3-4b49-a672-52b5b7ca5124", - "name": "Josteve Adekanbi" + "id": "5f8860b3-e669-4717-91bf-ddceb872fd55", + "name": "Benjamin Kadel" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185727, - "name": "Flutter" + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264369, + "name": "Other" + }, + { + "id": 264380, + "name": "Tooling" + }, + { + "id": 264394, + "name": "CI/CD" } ], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "520809", - "title": "TextField in Jetpack Compose: past, present and future", - "description": "Text input is a fundamental text component used in practically all apps at some point, from simple usage in a login screen to complex dynamic server-driven forms.\r\nIn this talk we’ll recap current text input APIs and their shortcomings, in particular for state management. As well we’ll explore the brand new BasicTextField2 API surface for common scenarios along with filtering, selection, gestures and more. The API you've been waiting for is coming to you soon :)", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "720685", + "title": "Why is adaptive layout a nightmare?", + "description": "Dive into the captivating realm of Android UI development in this session where we will share in-depth expertise on designing versatile user interfaces. Together, we will explore the complex challenges of organizing and building UI components, providing unique insights into crafting seamless user experiences across a variety of devices such as phones, foldables, and tablets.\r\n\r\nWhile Google advocates for the use of adaptive layout, we will unveil the often underestimated nuances, shed practical light on overcoming obstacles you’ll meet down the road and help you create truly adaptive Android applications.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "18c45d50-3e38-4933-a55b-8a833f850115", - "name": "Alejandra Stamato" + "id": "6520ad1e-8c4e-45c1-b34d-91ca73db27fe", + "name": "Gerard Paligot" }, { - "id": "d59b3a5b-f721-4356-a3e4-a2f86cb6fc50", - "name": "Anastasia Soboleva" + "id": "81f9ecbf-c08c-48c7-850f-1e79e1439867", + "name": "David Ta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264349, + "name": "Introductory and overview" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, + "id": 264358, "name": "Jetpack Compose" }, { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185728, + "id": 264364, + "name": "Compose" + }, + { + "id": 264367, + "name": "Foldables" + }, + { + "id": 264377, "name": "Modern Android Development" }, { - "id": 185735, + "id": 264384, "name": "Android" + }, + { + "id": 264390, + "name": "UI/UX" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "518266", - "title": "Making Data Visualizations More Accessible - Lessons Learned", - "description": "Graphs are a great way to visualize different types of data. But not everyone can consume them the same way - and if you add, e.g., touch interactions, not everyone can use the pointer the same way. Also, not everyone understands graphs the same way - due to prior familiarity, disability, or other reasons. And what if you can't see at all? That is another factor that adds complexity to data visualizations.\r\n\r\nIn this talk, I will share some tips and demonstrate how you can improve the accessibility of your graphs so that they work for different types of users - whether they are using assistive technologies or not. You'll get actionable advice to take to your apps and improve their accessibility immediately. You will learn about adding text alternatives for data visualization, adding alternatives for touch, and assuring that color is not the only way to communicate information.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "734218", + "title": "Why WhatsApp uses ListView", + "description": "Using outdated ListView on one of the most used screens screens sounds outrageous. Will talk about technical and organisational reasons why and how WhatsApp still uses ListView and what are the challenges preventing migration. ", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "6a7b0ee2-2d50-42b7-8225-1df145d255ea", - "name": "Eeva-Jonna Panula" + "id": "e11a53fd-1cfb-4930-9dfa-42187e94d869", + "name": "Hadi Tok" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264360, + "name": "Soft Skills" }, { - "id": 185714, - "name": "Accessibility" + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "495565", - "title": "Our ongoing journey from REST to GraphQL on Android", - "description": "Here is the story of an ongoing migration... and what a migration! This journey moving from our REST API to the GraphQL one is a long run with much coordination across all tech teams.\r\n\r\nThis return of experience will focus on how we are dealing with this transition on Android where Retrofit coexists with Apollo, how we synchronized with backend teams to keep our GraphQL schemas up-to-date or how we are dealing with our authentication stack with Apollo Kotlin, or the issues we faced and much more.\r\n\r\nBuckle up! Relax! The journey is just starting 🚜", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "741888", + "title": "Monetizing Your Side Project to $1k in Monthly Revenue and Beyond", + "description": "Only ~17% of apps reach $1k in monthly revenue. Learn best practices on crossing $1k MRR and beyond from personal experience building two profitable apps and advice from other successful app founders.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "462e6b97-01f9-44b1-864c-9d85bedf911b", - "name": "Julien Salvi" + "id": "0e4f3abe-cc6c-409a-8f34-e346f23c0cc4", + "name": "Jeffrey Bunn" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, + "id": 264360, + "name": "Soft Skills" + }, + { + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185724, - "name": "API" + "id": 264369, + "name": "Other" }, { - "id": 185735, - "name": "Android" + "id": 264381, + "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529650", - "title": "Honest mistakes caused by shallow interpretation of SOLID", - "description": "Although coding, in essence, is a clear subject that normally doesn’t open space for too many points of view with clear rights and wrongs, it’s naive to think that the same problem can’t be correctly solved by different architectures or even opposed solutions.\r\n\r\nBy taking SOLID concepts as an argument to support their cases, less experience engineers tend to not take context and product requirements into account, risking the failure of the initiative for the sake of following good practices by the book.\r\n\r\nMany times it’s also common that just couple principles are taken into account, completely ignoring others that might even be more important given the scope of the problem.\r\n\r\nThis talk is about recognizing these destructive patterns on your team when designing a solution to help you to Built it Right, as long as you are Building the Right Thing.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "719034", + "title": "Navigation in a Multiplatform World: Choosing the Right Framework for your App", + "description": "Navigation in mobile, desktop, and web applications is such a fundamental part of how we structure our architecture. In order to both obtain functional clarity, and abstraction from platform level implementation.\r\n\r\nFor a long time, there have been options available specific to each platform, and even options part of the platform framework itself. Though it can be difficult to find the right option for platform-agnostic code, ensuring consistency. Some go one step further, providing an opinionated guide on how to architecture your application.\r\n\r\nIn this talk, I'll evaluate the options available, how they differ, and to what type of applications they are best suited. Including how to get started with them, and the best practice guidelines on how to get the most out of them, for your application.", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b44c9f22-4d83-4246-bf67-bdd30af9bfef", - "name": "Haroldo Olivieri" + "id": "10659767-356c-4cd5-ba9c-12cec7a3ce79", + "name": "Ash Davies" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, - "name": "Other" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "522982", - "title": "Staying Relevant", - "description": "When working on legacy projects and slow to change codebases, it can feel like you are being left behind, while others are actively learning and using the latest technologies.\r\n\r\nI want to share my approach with you, to help keep on top of things and remove any FOMO.\r\n\r\nThe inspiration for this talk comes from working on a legacy project for the last couple of years.\r\nI’ve felt like I was becoming worse at my job, that I’m becoming outdated and falling behind. I want to help people not feel the way I felt.\r\nBeing at a conference seeing all the latest and greatest things can reinforce those feelings, so I will help bring everything back into perspective.\r\n\r\nSpecifically, I will outline a series of strategies you can follow, from day to day activities to thinking in the years of your career, and a specific set of recommendations for our current state in 2023.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "764400", + "title": "Ask Android! - Compose, WearOS, Platform", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T14:50:00", + "endsAt": "2024-11-01T15:30:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "b849afbd-1240-40e1-82ea-5660d3dca93b", - "name": "Neil Hutchison" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "2fb0b020-9fa9-49f1-b4a4-54f71dca57c5", + "name": "Kseniia Shumelchyk" + }, + { + "id": "9079b8b4-be1f-4213-a446-6b2bdcad0c04", + "name": "Fung Lam" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185709, - "name": "Soft Skills" - }, - { - "id": 185718, - "name": "Techniques & Guides" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528773", - "title": "Rolling in the deep(link) - take a deep dive into Flutter navigation", - "description": "Deep linking is an essential feature in mobile apps that allows users to access specific pages or sections of an app from an external source like a link or notification. \r\n\r\nIn this talk, I will cover everything you need to know about handling deep links in Flutter mobile apps. \r\n\r\nWe'll start by exploring an independent solution for handling deep links in Flutter, followed by discussing third-party solutions that offer additional features such as dynamic link generation and deferred deep links. We'll also examine the benefits and limitations of using these solutions and how to implement them in a Flutter app. \r\n\r\nThroughout the talk, we'll discuss common issues and solutions when handling deep links in Flutter apps. \r\n\r\nAttendees will leave with a comprehensive understanding of deep linking in Flutter and the knowledge to implement deep linking solutions in their mobile apps.", - "startsAt": "2023-10-27T14:20:00", - "endsAt": "2023-10-27T15:00:00", + "id": "764404", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:30:00", + "endsAt": "2024-11-01T15:45:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "d63cf645-9a0e-434b-bebe-4a3825755cb5", - "name": "Alicja Ogonowska" + "id": "b99dc2e2-df83-4045-a5cb-c4d7dea5db1e", + "name": "Halil Ozercan" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "b3baa5b6-3c85-4a36-97c5-9a252f887ae4", + "name": "Jelle Fresen" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "496334", - "title": "90s Website … in 2023 on mobile in Compose … for science", - "description": "Why would anyone build something with a 90s website aesthetic - in Compose? Nostalgia? For one the 90s website aesthetic made heavy use of animations and visual effects. So emulating this style will take any animation framework through its paces. This talk will take some of the most iconic 90s website elements and demonstrate how to reproduce them to build a retro mobile experience using Jetpack Compose. \r\n\r\nI can’t promise the end result will have a good user experience but I can promise a good look at the animation system in Compose and a collection of cheesy code snippets.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "717692", + "title": "Level up your SDKs with KMP - no rewrite required!", + "description": "There are several compelling reasons to explore adding Kotlin Multiplatform (KMP) to your existing SDKs or libraries. The main one - to reduce development time and cut maintenance costs by having a single codebase for shared logic.\r\n\r\nBut what if you already have a suite of tried and tested native SDKs, and want to simply add KMP capabilities to broaden your audience, without impacting existing customers? Or perhaps you’re adding functionality on top of your SDKs and want to build it once instead of duplicating it in 2, 3, or 4 programming languages?\r\n\r\nI’ll share our experiences at PubNub with bringing three of our realtime messaging SDKs - TypeScript, Swift and JVM, under a unified KMP API. I’ll also show how this foundation then enabled us to build and ship a new Chat SDK on top, built with KMP from the ground up and targeting all 3 platforms.\r\n\r\nThe talk will cover the initial requirements phase, the architectural decisions, and the tradeoffs that we had to make to finally land on KMP as the chosen solution.\r\n\r\nWe'll then delve into practical code examples and solutions (as well as quite a few WTF moments!) that allowed us to build the Chat SDK without affecting our existing users, and even improved the quality and consistency of our native SDKs.\r\n\r\n", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "2313d8eb-7d6e-444d-8e8d-4d223f896ce8", - "name": "Maia Grotepass" + "id": "ffd6d0b9-9615-4f5a-9f58-6dcf3d4acb3b", + "name": "Wojtek Kaliciński" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264359, + "name": "Kotlin" + }, + { + "id": 264361, + "name": "KMP" + }, + { + "id": 264374, + "name": "API" }, { - "id": 185732, + "id": 264381, "name": "Cross-Platform" } ], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "527245", - "title": "Multiplatform USB: How to Communicate Seamlessly", - "description": "At the droidcon Berlin we as the GDG Berlin Android introduced 'ZeBadge' a digital name badge, programmed by a companion Android app.\r\n\r\nSadly it was limited to only Android companions. But not anymore! Let me introduce ZeKompanion: A multiplatform app that communicates with ZeBadge, no matter the platform (except for iOS).\r\n\r\nThis talk will guide through the architecture of the app, describing the abstractions taken and explain how you can leverage Kotlin Multiplatform for Mobile to abstract the platform specific USB communication from the Compose UI displayed on all devices.\r\n\r\nFollow here for an interesting journey about USB Serial communication, Kotlin and KMM, Jetpack Compose and how to boil it all together into one app: ZeKompanion.\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "730918", + "title": "Upgrading Meta's Kotlin Infrastructure to K2: A Migration Journey", + "description": "In this talk, we will share our experience migrating Meta's Kotlin infrastructure from K1 to K2. We will discuss the challenges we faced during the migration process, including differences in error handling between K1 and K2, and how we overcame them. We will also cover the process of migrating compiler plugins, including overview of plugins we migrated and the solutions we implemented to overcome challenges. Finally, we will discuss our experience with the KSP2.0 migration.\r\nThis talk will provide valuable insights for developers and teams considering a migration to K2, as well as those interested in learning more about the challenges and solutions involved in large-scale migrations.\r\n\r\nKeywords: K2, Kotlin 2.0, Kotlin compiler plugins", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "fc80cf34-563e-4cd4-a04d-23d1191a7812", - "name": "Mario Bodemann" + "id": "33801296-8d41-4df0-b729-c0461afa1be2", + "name": "Ruslan Latypov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, - "name": "Intermediate" + "id": 264351, + "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185710, - "name": "KMP" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185720, - "name": "IoT" + "id": 264374, + "name": "API" }, { - "id": 185731, + "id": 264380, "name": "Tooling" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185730, - "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "528020", - "title": "Trust no one - Introducing the Play Integrity API", - "description": "You put a lot of time and effort into developing your application, but you're then releasing it into the wild without any security measures in place. Your app could be modified and requests to your backend server could be coming from unknown and unsafe environments, which makes your services vulnerable to attack and abuse. The Play Integrity API helps your app's backend server to take appropriate actions to prevent attacks and reduce abuse by detecting potentially risky and fraudulent interactions.\r\nIn this talk we will cover the API with a focus on:\r\n- How to setup and use the API\r\n- Low-latency use cases with the new standard request\r\n- Migrating from the SafetyNet Attestation API\r\n- How to test and manage errors\r\n", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "731808", + "title": "Streamlining Permission Request in Jetpack Compose", + "description": "Managing Android permission requests often involve considerable boilerplate code, and while Jetpack Compose offers many advantages, it has not simplified this aspect. It can add complexity if not properly designed. This talk will demonstrate an efficient method for creating a reusable Android permission request flow with minimal boilerplate using Jetpack Compose. By the end of this session, you will learn how to implement a reusable permission request handler for single and multiple permissions. Additionally, you'll discover how to provide custom permission rationale messages and design a custom permission request UI with minimal boilerplates, enhancing developer experience and application usability.\r\n\r\nKey Takeaways\r\n1. Understanding Permission Management in Android: A quick overview of the challenges in handling permission requests within the context of Android development.\r\n\r\n2. Identifying Boilerplate in Permission Requests: Examination of the typical boilerplate code associated with permission requests in Jetpack Compose.\r\n\r\n3. Efficient Permission Handling: Step-by-step guide on creating a reusable permission request handler for single and multiple permissions.\r\n\r\n4. Customization Techniques: How to implement custom permission rationale messages and design custom UI components for permission requests.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "e5941ea5-5a10-45a0-903b-f642db802a16", - "name": "Pietro Maggi" + "id": "3b95e79a-0e6b-4fcd-b44e-023fa3dfd7cd", + "name": "Mayowa Egbewunmi" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185735, - "name": "Android" + "id": 264358, + "name": "Jetpack Compose" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529128", - "title": "Navigation superpowers at your fingertips", - "description": "This talk will begin by the demonstration of a beautiful sample app built with Compose Mulitplatform and Appyx, complete with:\r\n\r\n- Shared element transitions\r\n- Scoped dependencies\r\n- Deep links that animate the app into a desired state\r\n- Gesture-controlled navigation\r\n\r\nWhile this is a wide range of topics, we’ll see how they belong together in one cohesive story, where they’re just different aspects of the same underlying theme: managing application state.\r\n\r\nIn this highly visual talk we’ll also see how using Appyx we can add the UI magic of custom transitions and gesture-control to our apps in no time.", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "735413", + "title": "Let's build a performance measuring tool from scratch", + "description": "Did you know you can measure the performance of production apps? Android is based on Linux, so that gives us a lot of power!\r\nThrough live-coding, let's have fun exploring system files, low-level events and the Android source code to see how we can gather metrics (FPS, CPU, RAM) on your favourite apps!\r\nYou'll gain more insights on how Android work under the hood and why measuring performance can be crucial but complex.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "94bb39bf-8256-4633-adcd-54d621242cea", - "name": "Zsolt Kocsi" + "id": "ae4dc258-34a6-462c-b778-807da7d25de4", + "name": "Alexandre Moureaux" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, + "id": 264351, "name": "Advanced" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185710, - "name": "KMP" - }, - { - "id": 185713, - "name": "Compose" - }, - { - "id": 185739, - "name": "Libraries" - }, - { - "id": 185741, - "name": "UI/UX" + "id": 264380, + "name": "Tooling" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530295", - "title": "Now smile (also in 3D)! Exploring AR, ML and Camera related APIs on Android", - "description": "When it comes to Camera APIs, Android has come a long way since its start, which is good, not only for the platform consistency above Lollipop but also because now we can increase its potential by using AR and ML APIs integrations, such as AR Core, ML Kit and MediaPipe. \r\n\r\nHowever, it can bit confusing if you're not familiar with the existing available APIs, which one to use for specific scenarios and how to approach Camera2, and CameraX in general.\r\n\r\nDuring this talk, you will learn the basics of camera APIs on Android and how to extend them to different use cases to enhance your app use experience!", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "709201", + "title": "Passwordless Future !!", + "description": "Passwords: no one likes them.\r\n\r\nWith so many requirements—capital letters, numbers, symbols—and the challenge of remembering a strong one, passwords often become more of a hassle than a security measure. Even the most complex passwords can be vulnerable to hackers who can guess, steal, or trick you into revealing them.\r\n\r\nThankfully, a better solution exists, which we will explore in today’s session: Passkeys.\r\n\r\nWhat You'll Learn:\r\n- An introduction to Passkeys and the primary reasons for their implementation.\r\n- Integration of Passkeys and demos into Android apps using the credential API.\r\n- How users can sign in to apps and websites using biometric sensors (fingerprint or facial recognition), PINs, or patterns, creating a seamless sign-in experience that eliminates the need for usernames or passwords.\r\n- Best practices for implementing Passkeys in your applications to maximize both security and user convenience.\r\n- Case studies and examples of successful Passkey integration in real-world applications.\r\n\r\nTarget Audience:\r\nThis talk is ideal for mobile developers of all levels who are interested in:\r\n- Smoothing the login experience for their users across various platforms.\r\n- Enhancing security for their apps.\r\n- Staying ahead with modern authentication methods to improve user retention and satisfaction.", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "226909f8-7c0a-43bc-b296-a3f2c369b9a3", - "name": "Walmyr Carvalho" + "id": "0037b354-6bd4-46e3-b1e6-d84916eea0d6", + "name": "Ayushi Gupta" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" + "id": 264368, + "name": "Techniques & Guides" }, { - "id": 185708, - "name": "Kotlin" + "id": 264377, + "name": "Modern Android Development" }, { - "id": 185728, - "name": "Modern Android Development" + "id": 264384, + "name": "Android" + }, + { + "id": 264385, + "name": "Design" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53982, + "room": "Nest", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "527829", - "title": "Seamless Interoperability: Harnessing JNIGen and FFIGen", - "description": "This talk dives into the world of seamless interoperability, unveiling the power of JNIGen and FFIGen in enabling direct access to native methods from Dart code. Unlike the current approach of using Platform channels in Flutter, JNIGen, and FFIGen allows us to achieve effortless access to native methods from Dart code. By eliminating the need for manual method handling and channel management, these tools revolutionize the development process. With practical examples and performance comparison, attendees will discover how JNIGen simplifies integration with Android libraries and JAVA classes, while FFIGen streamlines access to Swift and Objective-C libraries. ", - "startsAt": "2023-10-27T15:15:00", - "endsAt": "2023-10-27T15:55:00", + "id": "764406", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T15:45:00", + "endsAt": "2024-11-01T16:25:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "76ce51b1-7dd4-4858-98de-0b51b3c9abcb", - "name": "Akanksha Singh" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" }, { - "id": "d9cf6863-3524-4461-b5d2-ee91cc1595f2", - "name": "Kendi J" + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185718, - "name": "Techniques & Guides" - }, - { - "id": 185727, - "name": "Flutter" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "538661", - "title": "A guide to using foreground services and background work in Android 14", - "description": "Coroutines, WorkManager, Foreground Services, Oh my! \r\nHave you ever felt confused on how to ensure your application is kept alive to continue work after it's left the foreground?\r\n\r\nThere have been many changes over the past few Android releases regarding the background work and Foreground Services. In this talk we will break down: \r\n- What is background work vs foreground service vs asynchronous work?\r\n- What’s new with Foreground Services in Android 14?\r\n- We'll provide a framework to help you decide what API to use in Android 14\r\n- We'll go over some common misconceptions around WorkManager or Foreground Services\r\n\r\nAs a bonus we’ll also discuss what the future of managing background work and Foreground Services will look like beyond Android 14.", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764408", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:25:00", + "endsAt": "2024-11-01T16:40:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "05c58f24-23c5-4bce-a4f5-9b610f7084d3", - "name": "Alice Yuan" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 36716, - "room": "Lovelace", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530286", - "title": "From Laptop Builds to Advanced CI", - "description": "How do you transition from the solo-coder mindset to building a robust, automated CI pipeline that supercharges your team?\r\n\r\nOnce you get a basic pipeline running there are numerous aspects to evaluate:\r\n\r\n* Are the results useful?\r\n* You start to add unit test results and collect build artifacts. Is it fast and reliable enough that the team benefits from it?\r\n* Do you need a merge queue or do you need better checks?\r\n* What is consistently missed that you can automate?\r\n* What tedious repetitive tasks can you transform into a welcome resource?\r\n* Are you hitting resource limits that require an engineering investment to mitigate?\r\n* Do you invest in Gradle or Bazel? What JVM options are right for your codebase?\r\n* Is Docker useful? How far should you go in your optimizations?\r\n* How do you secure the pipeline?\r\n \r\nWe'll delve into each topic and share how to apply our learnings to empower you. Along the way we will discuss how to approach stakeholders outside engineering to demonstrate the value it brings to a business. Join us for a saga of struggles and victories and how we transformed our CI pipeline at a modern scale-up business.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "732470", + "title": "Exploring Kotlin Symbol Processing: A Practical Guide", + "description": "In this session we’ll dive into the world of Kotlin Symbol Processing (KSP). This session aims to provide an introduction to KSP and its benefits compared to the Kotlin Annotation Processing Tool (KAPT).\r\n\r\nThe practical portion of this talk will guide you through the process of creating annotation definitions and implementing a symbol processor. We will demonstrate the usage of KSP API and KotlinPoet for generating Kotlin files, providing you with a hands-on experience of working with KSP. Furthermore, we will demonstrate how to use KSP in multiplatform projects.\r\n\r\nBy the end of this talk, you will walk away with a solid understanding of Kotlin Symbol Processing, and practical knowledge on how to leverage KSP in your development workflow.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "96e519b2-b0e1-4232-a71c-21bf3145cb13", - "name": "Jason Pearson" + "id": "f589d95b-8262-4337-b6c8-78b4701afa3a", + "name": "Dean Djermanović" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185701, - "name": "Advanced" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, + "id": 264359, "name": "Kotlin" - }, - { - "id": 185712, - "name": "Testing" - }, - { - "id": 185716, - "name": "Modularization" - }, - { - "id": 185728, - "name": "Modern Android Development" - }, - { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185730, - "name": "Android Studio" } ], "sort": 4 } ], - "roomId": 36717, - "room": "Hamilton", + "roomId": 49270, + "room": "Hangouts", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "510034", - "title": "Hot Take: Engineering Managers are actually useful!", - "description": "Is your manager pulling their weight, or just a waste of space? Should they exist just to protect the people doing the *actual work* from ignorant execs? \r\n\r\nTruthfully, managing any human is a science and an art, and managing engineers certainly isn't easier! A good manager is a leader, architect, consiglieri, and career coach… and yes, sometimes the person who moves JIRA tickets around a kanban board!\r\n\r\nIn this talk, we'll address some common hot takes about engineering management and talk about the transition from engineer to manager. Whether you want to become a manager or continue as an individual contributor, you'll leave with a better sense of what your manager actually does!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "735915", + "title": "Android driver - application of Android device in RC vehicle development", + "description": "TL;DR: This talk will showcase development of remote controlled vehicle with Android powered device as its central unit. \r\n\r\n... and now long version:\r\nStudents and researches around the world often struggle in early stages of development of robotics and hardware projects. The reason? High cost and connection difficulties of all the sensors and effectors for Arduinos, Raspberry Pi's or whatever platform they do choose. All of them have a solution right in their hands - or their pockets - and that is with almost no additional costs. \r\n\r\nBy enabling Android device to act as a part, or center, of embedded system you can unlock vast variety or its capabilities - ones that would cost hundreds of dollars to purchase and dozens of hours to connect & set up. Some of them are:\r\n- LTE access to Internet\r\n- WiFi & bluetooth connectivity\r\n- screen, speakers, microphone, LED light\r\n- accelerometer, gyroscope\r\n- multiple cameras\r\n- biometric sensors\r\nNot to mention higher level of services they offer through countless libraries ready to be used by developers.\r\n\r\n-> By the end of this talk, attendees will discover an innovative & easy way to use Android powered devices to cut down costs of prototyping hardware projects, especially as proposed, by building RC vehicle. ", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "7a816772-cc23-42e7-818e-4fd5153f7283", - "name": "Parth Padgaonkar" + "id": "58038e33-703e-497c-9f47-3624e36418e8", + "name": "Tomasz Słuszniak" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185719, + "id": 264366, + "name": "Modularization" + }, + { + "id": 264368, + "name": "Techniques & Guides" + }, + { + "id": 264369, "name": "Other" + }, + { + "id": 264370, + "name": "IoT" + }, + { + "id": 264384, + "name": "Android" } ], "sort": 4 } ], - "roomId": 36718, - "room": "Liskov", + "roomId": 49271, + "room": "Glass", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530595", - "title": "Sink or swim: proving ideas in production, fast", - "description": "It all began with a beer and a video call: a group of friends had an app idea and wanted to see whether it could work out. But going from idea to shipping something on the Play Store isn’t that easy. How do you organize work? What are the roles and tools to use? And most importantly, how to build a framework onto which you can iterate on multiple ideas, fast?\r\n\r\nThat’s what we had to figure out over the past six months as we launched multiple ideas into production. Join us in a retrospective of how we got from that famous beer to today. We’ll cover how an Android dev such as yourself can organize work to prioritize fast iteration, pick the right tools and technologies, automate the heck out of everything, figure out how to promote and grow the app, plan a business strategy, and understand if it is sustainable.\r\n\r\nBy attending this session, you’ll find out what worked out for us and what didn’t in this process, and how to avoid some mistakes we made. If you’ve ever had the itch to publish an app — by yourself or with some friends — come along to find out what’s ahead of you!", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "734441", + "title": "A journey in Android's BLE world", + "description": "Working with BLE on Android could be overwhelming, there are a lot of OSS libraries to pick from, different permissions to be declared, and sometimes different behaviors depending on the Android version and device.\r\n\r\nThis talk aims to make order between what's available out there: we'll start from the basics of how BLE works, climbing the ladder and going more high-level examining tools and libraries using Kotlin features to simplify observing data and freeing up unused resources, including an exploration of latest JetPack library AndroidX Bluetooth.\r\n\r\nYou'll find real-life scenarios, examples, and strange issues you may encounter (along with even stranger fixes).\r\n\r\nWhether you plan to integrate a BLE device in your app, improve the existing code to be more expressive and use modern libraries, or even only scan for Bluetooth beacons, follow me on this journey in the BLE world.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "83d33e86-8cca-44a8-b0dc-2994332ef8e8", - "name": "Ivan Morgillo" - }, - { - "id": "8fc4211d-38ef-4661-acee-16b4ea9cc800", - "name": "Aurelio Laudiero" - }, - { - "id": "f897009d-854d-4403-ab47-44c29d8edce1", - "name": "Daniele Bonaldo" + "id": "8253d551-1437-4a3e-b4f4-56bf73373109", + "name": "Paolo Rotolo" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185700, + "id": 264350, "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185707, - "name": "Jetpack Compose" - }, - { - "id": 185708, + "id": 264359, "name": "Kotlin" }, { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185724, + "id": 264374, "name": "API" }, { - "id": 185728, + "id": 264377, "name": "Modern Android Development" }, { - "id": 185731, - "name": "Tooling" - }, - { - "id": 185745, - "name": "CI/CD" - }, - { - "id": 185733, - "name": "AI/ML" - }, - { - "id": 185735, - "name": "Android" - }, - { - "id": 185737, - "name": "Coroutines" - }, - { - "id": 185729, - "name": "Gradle" - }, - { - "id": 185742, - "name": "Firebase" + "id": 264388, + "name": "Libraries" } ], "sort": 4 } ], - "roomId": 39640, - "room": "Hopper", + "roomId": 49272, + "room": "Things", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "529420", - "title": "Unlocking Unity for Android Developers: Blending Android and Unity in Harmony", - "description": "We've all become accustomed to working on Android projects as part of our daily work. But have you ever thought about how you could seamlessly adapt your existing solutions to play nice with other engines like Unity? Or perhaps you’re considering creating an entirely new library to incorporate functionalities that aren't readily available in the core Unity. \r\n\r\nSimilarly, Unity paves the way for hybrid Android development, allowing you to embed Unity-created elements, mini-games and augmented reality directly into your Android apps.\r\n\r\nIn this talk, we're delving into the Unity landscape from an Android developer's perspective. We'll unravel the intricate interplay between Unity scripts and Android code, gaining insights into how IL2CPP and Mono scripting backends operate to seamlessly unite C# and Kotlin.\r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "706060", + "title": "Overcoming Unsecurities in WebViews", + "description": "Is your relationship with WebViews healthy? Sometimes you can't avoid the need to display web content in your app. It can be a functionality that you need to release quickly and it's already implemented by web devs in your team. It can be just a Terms and Conditions page you need to show. Often the reason for putting these into WebViews is that the latest version must be displayed without requiring an app update.\r\n\r\nSo web content tends to make its way into many apps. It's not obvious that by adding a single WebView, you can open up your app for abuse by malicious actors. Google made steady progress in making WebViews more secure by default but often you can't stop supporting those old, vulnerable OS versions. Ultimately it's your responsibility to secure your WebViews and the default settings are not always right. This talk aims to help with that while also highlighting security issues that lurk in those seemingly simple yet quite complex APIs.\r\n\r\nYou would learn the importance of always sanitizing inputs and restricting capabilities to what is actually needed. If you want to take one piece of advice from the talk, you should use more modern APIs like Custom Tabs, JavaScript Engine, or AndroidX's WebView variant.", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "bb2df6d6-edb0-416a-85fc-83b062c6c10c", - "name": "Yevhen Pekutovskyi" + "id": "6b808c1f-836d-4349-9585-a89355dfb8d0", + "name": "Balázs Gerlei" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, + "id": 264354, "name": "Session" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", "categoryItems": [ { - "id": 185708, - "name": "Kotlin" - }, - { - "id": 185718, + "id": 264368, "name": "Techniques & Guides" }, { - "id": 185719, - "name": "Other" - }, - { - "id": 185735, - "name": "Android" + "id": 264372, + "name": "Security" } ], "sort": 4 } ], - "roomId": 39641, - "room": "Booth", + "roomId": 53981, + "room": "Stadia", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true }, { "questionAnswers": [], - "id": "530074", - "title": "Bring the power of Postgres to your Flutter app with Supabase", - "description": "Supabase is an open-source Firebase alternative centered around Postgres database, one of the world’s most popular databases known for its extensibility, performance, and data integrity. You can create a fresh Supabase project with just a few clicks, and you will gain access to a fully managed Postgres database with serverless APIs, real-time database subscription, auth, file storage, and edge functions. With its Flutter SDK, developers can access the Supabase database securely and efficiently, helping them build complex apps in a matter of days. \r\n\r\nIn this session, I will give you an overview of what Supabase is, and how it is helping developers build scalable applications efficiently. We will also look at a few examples where advanced data types of Postgres, such as geo data, or range data, can help you build complex Flutter applications efficiently. \r\n", - "startsAt": "2023-10-27T16:10:00", - "endsAt": "2023-10-27T16:50:00", + "id": "764409", + "title": "Ask Android! - Compose, Gemini in AS, KMP", + "description": "Join members of the Google DevRel and Engineering teams to get answers to your most pressing questions in 1:1 office hours sessionsl", + "startsAt": "2024-11-01T16:40:00", + "endsAt": "2024-11-01T17:20:00", "isServiceSession": false, "isPlenumSession": false, "speakers": [ { - "id": "31f0ce3c-630f-4146-a6f5-9e6c7f5cad62", - "name": "Tyler Shukert" + "id": "7a8a22c6-5b01-4f0a-bc04-12d4ada5de21", + "name": "Jossi Wolf" + }, + { + "id": "b5b73a2f-d57d-4a42-b787-a93f4ca9ba79", + "name": "Simona Milanovic" + }, + { + "id": "7280b9e7-ddf7-4cfb-b85b-1e998d851f66", + "name": "Marcello Galhardo" + }, + { + "id": "eb4483f1-306b-447d-9888-648ec0fc0018", + "name": "Chris Sinco" + }, + { + "id": "f4580454-6603-44a8-9fcd-e98fe6cc8981", + "name": "Matvei Malkov" } ], "categories": [ { - "id": 53527, + "id": 74391, "name": "Level", "categoryItems": [ { - "id": 185699, - "name": "Introductory and overview" + "id": 264350, + "name": "Intermediate" } ], "sort": 0 }, { - "id": 53528, + "id": 74392, "name": "Session format", "categoryItems": [ { - "id": 185704, - "name": "Session" + "id": 306233, + "name": "Office Hours" } ], "sort": 1 }, { - "id": 53529, + "id": 74393, "name": "Tags", - "categoryItems": [ - { - "id": 185724, - "name": "API" - }, - { - "id": 185727, - "name": "Flutter" - }, - { - "id": 185739, - "name": "Libraries" - } - ], + "categoryItems": [], "sort": 4 } ], - "roomId": 39642, - "room": "Lamarr", + "roomId": 54706, + "room": "Ask Android", "liveUrl": null, "recordingUrl": null, - "status": "Accepted" + "status": "Accepted", + "isInformed": true, + "isConfirmed": true } ], "isDefault": false diff --git a/shared/src/commonMain/resources/sponsors.json b/shared/src/commonMain/resources/sponsors.json index 3d1f9ebbc..2bcd9bc9e 100644 --- a/shared/src/commonMain/resources/sponsors.json +++ b/shared/src/commonMain/resources/sponsors.json @@ -1,7 +1,7 @@ { "documents": [ { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/bronze", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/Community Partners", "fields": { "sponsors": { "arrayValue": { @@ -9,108 +9,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Appvestor" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/appvestor.png" - }, - "url": { - "stringValue": "https://appvestor.com" - } - } - } - }, - { - "mapValue": { - "fields": { - "name": { - "stringValue": "Gradle" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/Gradle.png" - }, - "url": { - "stringValue": "https://gradle.com/" - } - } - } - }, - { - "mapValue": { - "fields": { - "name": { - "stringValue": "Marks & Spencer" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/MS.png" - }, - "url": { - "stringValue": "https://jobs.marksandspencer.com/" - } - } - } - }, - { - "mapValue": { - "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/04/runway-team.png" - }, - "name": { - "stringValue": "Runway" - }, - "url": { - "stringValue": "https://www.runway.team/" - } - } - } - }, - { - "mapValue": { - "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/06/uber-1.png" - }, - "name": { - "stringValue": "Uber" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/codeskillbuddy.png" }, "url": { - "stringValue": "https://www.uber.com/de/en/" - } - } - } - } - ] - } - }, - "displayOrder": { - "integerValue": "3" - } - }, - "createTime": "2023-10-12T11:42:43.670752Z", - "updateTime": "2023-10-12T17:07:38.816219Z" - }, - { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/community partners", - "fields": { - "displayOrders": { - "integerValue": "8" - }, - "sponsors": { - "arrayValue": { - "values": [ - { - "mapValue": { - "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/codeskillbuddy.png" + "stringValue": "https://www.meetup.com/londoncodeskillbuddy/?_cookie-check=7795cfGPfQ_U08aG" }, "name": { "stringValue": "Code Skill Buddy" - }, - "url": { - "stringValue": "https://www.meetup.com/londoncodeskillbuddy/?_cookie-check=7795cfGPfQ_U08aG" } } } @@ -121,11 +27,11 @@ "icon": { "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/06/Coder-bee.png" }, - "name": { - "stringValue": "Coderbee" - }, "url": { "stringValue": "https://coderbee.de/" + }, + "name": { + "stringValue": "Coder Bee" } } } @@ -149,13 +55,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/08/code-with-italians.jpeg" - }, - "name": { - "stringValue": "Code with the Italians" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/code-with-italians-1024x1024-1.png" }, "url": { "stringValue": "https://codewiththeitalians.it/" + }, + "name": { + "stringValue": "Code With The Italians" } } } @@ -164,13 +70,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Flutter London " - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/lutter-london.png" + "stringValue": "Flutter London" }, "url": { "stringValue": "https://flutterldn.dev/" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/lutter-london.png" } } } @@ -178,14 +84,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/03/groundbreaker.png" - }, "name": { - "stringValue": "Groundbreaker" + "stringValue": "GroundBreaker" }, "url": { "stringValue": "https://groundbreaker.org/" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/03/groundbreaker.png" } } } @@ -193,12 +99,12 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/gdg-london.png" - }, "name": { "stringValue": "GDG London" }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/gdg-london.png" + }, "url": { "stringValue": "https://gdg.community.dev/gdg-london/" } @@ -208,14 +114,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Londroid" - }, "icon": { "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/08/londroid.png" }, "url": { "stringValue": "https://www.meetup.com/android/" + }, + "name": { + "stringValue": "Londroid" } } } @@ -223,14 +129,14 @@ { "mapValue": { "fields": { + "name": { + "stringValue": "stickermule" + }, "icon": { "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/05/Sticker-Mule.png" }, - "name": { - "stringValue": "Stickermule " - }, "url": { - "stringValue": "https://www.stickermule.com/uk/uses/laptop-stickers?utm_source=google&utm_campaign=droidconlondon23&utm_id=droidconlondon23" + "stringValue": "https://www.stickermule.com/uk/custom-stickers" } } } @@ -238,14 +144,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/women-who-code.png" - }, "name": { - "stringValue": "Woman who code" + "stringValue": "Women Who Code" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/03/WWCode_FullLogo_Black.png" }, "url": { - "stringValue": "https://www.womenwhocode.com/opportunities/" + "stringValue": "https://womenwhocode.com/" } } } @@ -254,48 +160,48 @@ } }, "displayOrder": { - "integerValue": "8" + "integerValue": "6" } }, - "createTime": "2023-10-12T11:50:19.409338Z", - "updateTime": "2023-10-16T23:29:01.013279Z" + "createTime": "2024-10-15T07:13:40.420091Z", + "updateTime": "2024-10-18T13:44:22.990545Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/diversity sponsor", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/Media Partners", "fields": { + "displayOrder": { + "integerValue": "9" + }, "sponsors": { "arrayValue": { "values": [ { "mapValue": { "fields": { - "name": { - "stringValue": "Bumble" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/bumble.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/devit.png" + }, + "name": { + "stringValue": "devITjobs" }, "url": { - "stringValue": "https://bumble.com/" + "stringValue": "https://devitjobs.uk/jobs/Mobile/all" } } } } ] } - }, - "displayOrder": { - "integerValue": "7" } }, - "createTime": "2023-10-12T11:49:26.693797Z", - "updateTime": "2023-10-12T16:47:56.350174Z" + "createTime": "2024-10-15T09:21:13.732193Z", + "updateTime": "2024-10-15T09:23:16.971267Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/gold", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/Speaker Party", "fields": { "displayOrder": { - "integerValue": "1" + "integerValue": "7" }, "sponsors": { "arrayValue": { @@ -304,28 +210,44 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/embrace.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/03/Monzo_logo.png" }, "name": { - "stringValue": "Embrace" + "stringValue": "Monzo" }, "url": { - "stringValue": "https://embrace.io/" + "stringValue": "https://monzo.com/" } } } - }, + } + ] + } + } + }, + "createTime": "2024-10-15T08:16:52.019753Z", + "updateTime": "2024-10-15T08:21:07.556460Z" + }, + { + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/Speaker Sponsors", + "fields": { + "displayOrder": { + "integerValue": "8" + }, + "sponsors": { + "arrayValue": { + "values": [ { "mapValue": { "fields": { - "name": { - "stringValue": "Licel" + "url": { + "stringValue": "https://www.apptaura.com/" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/licel.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/09/Apptaura_mts_Red.png" }, - "url": { - "stringValue": "https://licelus.com/" + "name": { + "stringValue": "Apptaura" } } } @@ -334,13 +256,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/mango-db.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/09/PATREON_WORDMARK_1_BLACK_RGB-1-1024x348.png" }, "name": { - "stringValue": "MongoDB" + "stringValue": "Patreon" }, "url": { - "stringValue": "https://www.mongodb.com" + "stringValue": "https://www.patreon.com/" } } } @@ -349,28 +271,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Revenuecat" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/02/revenuecat.png" + "stringValue": "Uber" }, "url": { - "stringValue": "https://www.revenuecat.com/" - } - } - } - }, - { - "mapValue": { - "fields": { - "name": { - "stringValue": "Touchlab" + "stringValue": "https://www.uber.com/de/de/" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/touchlab.png" - }, - "url": { - "stringValue": "https://touchlab.co/" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/2560px-Uber_logo_2018.svg-1-1024x356-1.png" } } } @@ -378,61 +285,30 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Tricentis" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/tricentis.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/RS8288_ATLogo_RGB_Full_Colour_hpr-1024x124.png" }, - "url": { - "stringValue": "https://www.tricentis.com/" - } - } - } - } - ] - } - } - }, - "createTime": "2023-10-12T11:42:07.332761Z", - "updateTime": "2023-10-12T13:21:19.745687Z" - }, - { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/lanyard", - "fields": { - "sponsors": { - "arrayValue": { - "values": [ - { - "mapValue": { - "fields": { "name": { - "stringValue": "Algolia" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/algolia-1.png" + "stringValue": "AutoTrader" }, "url": { - "stringValue": "https://www.algolia.com/" + "stringValue": "https://careers.autotrader.co.uk/" } } } } ] } - }, - "displayOrder": { - "integerValue": "5" } }, - "createTime": "2023-10-12T11:47:46.820580Z", - "updateTime": "2023-10-12T16:45:13.258441Z" + "createTime": "2024-10-15T08:21:45.155946Z", + "updateTime": "2024-10-18T13:58:39.426861Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/media partners", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/Video Sponsor", "fields": { "displayOrder": { - "integerValue": "10" + "integerValue": "5" }, "sponsors": { "arrayValue": { @@ -441,28 +317,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/devit.png" - }, - "name": { - "stringValue": "DevIT" - }, - "url": { - "stringValue": "https://devitjobs.uk/jobs/Mobile/all" - } - } - } - }, - { - "mapValue": { - "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/globaltech.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/09/amex.png" }, "name": { - "stringValue": "Global techgadgets" + "stringValue": "American Express" }, "url": { - "stringValue": "https://globaltechgadgets.com/" + "stringValue": "https://www.americanexpress.com/en-us/careers/?inav=footer_careers" } } } @@ -471,14 +332,14 @@ } } }, - "createTime": "2023-10-12T11:52:09.880347Z", - "updateTime": "2023-10-12T17:05:05.382652Z" + "createTime": "2024-10-15T06:46:48.830556Z", + "updateTime": "2024-10-15T07:00:53.785973Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/platinum", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/bronze", "fields": { "displayOrder": { - "integerValue": "0" + "integerValue": "3" }, "sponsors": { "arrayValue": { @@ -486,45 +347,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Amazon Appstore" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/amazonappstore.png" - }, "url": { - "stringValue": "https://developer.amazon.com/apps-and-games" - } - } - } - } - ] - } - } - }, - "createTime": "2023-10-12T11:41:55.816259Z", - "updateTime": "2023-10-12T13:00:37.310484Z" - }, - { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/silver", - "fields": { - "displayOrder": { - "integerValue": "2" - }, - "sponsors": { - "arrayValue": { - "values": [ - { - "mapValue": { - "fields": { + "stringValue": "https://appvestor.com/" + }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/appcircle.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/appvestor.png" }, "name": { - "stringValue": "Appcircle" - }, - "url": { - "stringValue": "https://appcircle.io/" + "stringValue": "Appvestor" } } } @@ -533,13 +363,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/bitrise.png" - }, - "name": { - "stringValue": "Bitrise" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/New-Project.png" }, "url": { - "stringValue": "https://bitrise.io/solutions/technologies/android" + "stringValue": "https://flo.health/" + }, + "name": { + "stringValue": "Flo" } } } @@ -548,13 +378,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Build38" + "stringValue": "Gradle" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/build.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/05/LOGO-GRADLE-HZ_MONO.png" }, "url": { - "stringValue": "https://build38.com" + "stringValue": "https://gradle.com/" } } } @@ -563,13 +393,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/bumble.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/04/runway-team.png" }, "name": { - "stringValue": "Bumble" + "stringValue": "Runway" }, "url": { - "stringValue": "https://bumble.com" + "stringValue": "https://www.runway.team/" } } } @@ -577,14 +407,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Ditto" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/ditto.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/03/Monzo_logo.png" }, "url": { - "stringValue": "https://ditto.live/" + "stringValue": "https://monzo.com/" + }, + "name": { + "stringValue": "Monzo" } } } @@ -592,29 +422,42 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/07/Guardsquare.png" - }, "name": { - "stringValue": "Guardsquare" + "stringValue": "M&S" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/09/M_S_SquareLogo_Masterbrand_Primary_Blk_RGB-400x400.png" }, "url": { - "stringValue": "https://www.guardsquare.com/dexguard" + "stringValue": "https://jobs.marksandspencer.com/" } } } - }, + } + ] + } + } + }, + "createTime": "2024-10-14T20:32:27.979615Z", + "updateTime": "2024-10-18T13:53:59.355640Z" + }, + { + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/gold", + "fields": { + "sponsors": { + "arrayValue": { + "values": [ { "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/06/Instabug.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/04/cropped-Appdome_Logo_Positive-qai7m70urathe6hh63awjb6o5hl9o4p3z7qn9qutq8.png" }, "name": { - "stringValue": "Instabug" + "stringValue": "Appdome" }, "url": { - "stringValue": "https://www.instabug.com/" + "stringValue": "https://www.appdome.com/" } } } @@ -623,13 +466,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Openbank" + "stringValue": "Ditto" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/openbank.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/03/download.png" }, "url": { - "stringValue": "https://www.openbank.es/ofertas-empleo-it" + "stringValue": "https://ditto.live/" } } } @@ -637,14 +480,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads" + "url": { + "stringValue": "https://www.google.com/" }, "name": { - "stringValue": "PACE AP" + "stringValue": "Google" }, - "url": { - "stringValue": "https://paceap.com/" + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/06/Google_2015_logo.svg-2.png" } } } @@ -652,14 +495,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/sentry.png" - }, "name": { - "stringValue": "Sentry" + "stringValue": "Licel" }, "url": { - "stringValue": "https://sentry.io/welcome/" + "stringValue": "https://licelus.com/" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/Licel-1024x538.png" } } } @@ -668,13 +511,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Sofy" + "stringValue": "Meta" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/02/sofy.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/Meta_Lockup_PositivePrimary_RGB-1024x441.png" }, "url": { - "stringValue": "https://sofy.ai/" + "stringValue": "https://developers.meta.com/horizon" } } } @@ -682,14 +525,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/05/Vonage.png" - }, "name": { - "stringValue": "Vonage" + "stringValue": "Qualcomm" }, "url": { - "stringValue": "https://developer.vonage.com/en/home" + "stringValue": "https://www.qualcomm.com/developer" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/09/Qualcomm-Logo.wine_-1024x683-1.png" } } } @@ -697,14 +540,14 @@ { "mapValue": { "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/10/zebra-2.png" - }, "name": { - "stringValue": "Zebra" + "stringValue": "RevenueCat" }, "url": { - "stringValue": "https://developer.zebra.com/" + "stringValue": "https://www.revenuecat.com/" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/02/revenuecat.png" } } } @@ -712,42 +555,48 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Zimperium" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/07/zimperium.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/Touchlab_Wordmark-1-1-1-1-1.png" + }, + "name": { + "stringValue": "Touchlab" }, "url": { - "stringValue": "https://www.zimperium.com/" + "stringValue": "https://touchlab.co/" } } } } ] } + }, + "displayOrder": { + "integerValue": "1" } }, - "createTime": "2023-10-12T11:42:30.205255Z", - "updateTime": "2023-10-12T15:05:10.160124Z" + "createTime": "2024-10-14T02:09:33.515036Z", + "updateTime": "2024-10-18T13:35:12.512856Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/speaker sponsors", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/silver", "fields": { + "displayOrder": { + "integerValue": "2" + }, "sponsors": { "arrayValue": { "values": [ { "mapValue": { "fields": { + "url": { + "stringValue": "https://www.bitrise.io/why/technologies/android-continuous-integration" + }, "name": { - "stringValue": "Esri" + "stringValue": "Bitrise" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/esri.png" - }, - "url": { - "stringValue": "https://developers.arcgis.com/kotlin/" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/bitrise.png" } } } @@ -756,13 +605,13 @@ "mapValue": { "fields": { "name": { - "stringValue": "Justeattakeaway" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/just-eat.png" + "stringValue": "Otka" }, "url": { - "stringValue": "https://careers.justeattakeaway.com/global/en/c/tech-product-jobs" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/Auth0byOkta_stack_rgb_blk.png" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/Auth0byOkta_stack_rgb_blk.png" } } } @@ -770,14 +619,14 @@ { "mapValue": { "fields": { + "url": { + "stringValue": "https://paceap.com/" + }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/labrys.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/06/pace.png" }, "name": { - "stringValue": "Labrys" - }, - "url": { - "stringValue": "https://www.labrys.tech" + "stringValue": "Pace Anti Privacy" } } } @@ -786,13 +635,13 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/oura.png" - }, - "name": { - "stringValue": "Oura" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/sentry.png" }, "url": { - "stringValue": "https://ouraring.com/" + "stringValue": "https://sentry.io/welcome/" + }, + "name": { + "stringValue": "Sentry" } } } @@ -800,14 +649,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Perry Street" + "url": { + "stringValue": "https://developer.vonage.com/" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/perry.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/VonagePOE_Primary-1024x270.png" }, - "url": { - "stringValue": "https://www.perrystreet.com/" + "name": { + "stringValue": "Vonage" } } } @@ -816,29 +665,41 @@ "mapValue": { "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/09/supabase.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/zebra-logo-1-1024x398-1.png" }, "name": { - "stringValue": "Supabase" + "stringValue": "Zebra Developers" }, "url": { - "stringValue": "https://supabase.com/" + "stringValue": "https://developer.zebra.com/" + } + } + } + }, + { + "mapValue": { + "fields": { + "name": { + "stringValue": "Zimperium" + }, + "url": { + "stringValue": "https://www.zimperium.com/" + }, + "icon": { + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/07/zimperium.png" } } } } ] } - }, - "displayOrder": { - "integerValue": "9" } }, - "createTime": "2023-10-12T11:51:11.084894Z", - "updateTime": "2023-10-12T17:03:31.355745Z" + "createTime": "2024-10-14T20:17:39.777551Z", + "updateTime": "2024-10-18T13:37:55.546002Z" }, { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/startup alley", + "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2024/startup alley", "fields": { "displayOrder": { "integerValue": "4" @@ -849,14 +710,14 @@ { "mapValue": { "fields": { + "url": { + "stringValue": "https://www.emergetools.com/" + }, "name": { "stringValue": "Emerge Tools" }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/emerge-tools.png" - }, - "url": { - "stringValue": "https://www.emergetools.com" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/10/emerge_tools_vertical_black-1.png" } } } @@ -864,44 +725,14 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Glassfy " - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/03/glassfy.png" - }, "url": { - "stringValue": "https://glassfy.io/" - } - } - } - }, - { - "mapValue": { - "fields": { - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/06/marathon.png" - }, - "name": { - "stringValue": "Marathon Labs" + "stringValue": "https://screenshotbot.io/" }, - "url": { - "stringValue": "https://marathonlabs.io/" - } - } - } - }, - { - "mapValue": { - "fields": { "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/08/monedata-1.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/screenshotbot.png" }, "name": { - "stringValue": "Monedata" - }, - "url": { - "stringValue": "https://monedata.io/" + "stringValue": "ScreenshotBot" } } } @@ -909,55 +740,24 @@ { "mapValue": { "fields": { - "name": { - "stringValue": "Screenshotbot" - }, "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2023/10/screenshotbot.png" + "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2024/09/Tram_licensed.png" }, - "url": { - "stringValue": "https://screenshotbot.io/" - } - } - } - } - ] - } - } - }, - "createTime": "2023-10-12T11:43:06.422763Z", - "updateTime": "2023-10-12T15:31:45.390791Z" - }, - { - "name": "projects/droidcon-148cc/databases/(default)/documents/sponsors-london-2023/video", - "fields": { - "sponsors": { - "arrayValue": { - "values": [ - { - "mapValue": { - "fields": { "name": { - "stringValue": "Amex" - }, - "icon": { - "stringValue": "https://london.droidcon.com/wp-content/uploads/sites/3/2022/09/amex.png" + "stringValue": "Tramline" }, "url": { - "stringValue": "https://www.americanexpress.com/en-us/careers/?inav=footer_careers" + "stringValue": "https://www.tramline.app/" } } } } ] } - }, - "displayOrder": { - "integerValue": "6" } }, - "createTime": "2023-10-12T11:52:40.427665Z", - "updateTime": "2023-10-12T16:46:31.701800Z" + "createTime": "2024-10-15T06:38:57.924702Z", + "updateTime": "2024-10-18T13:41:46.683696Z" } ] } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt index 196f699ee..6a75154eb 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt @@ -12,14 +12,15 @@ import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.darwin.Darwin import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ObjCClass +import kotlinx.cinterop.ObjCObject import kotlinx.cinterop.ObjCProtocol import kotlinx.cinterop.getOriginalKotlinClass import org.koin.core.Koin +import org.koin.core.parameter.ParametersDefinition import org.koin.core.parameter.parametersOf import org.koin.core.qualifier.Qualifier import org.koin.dsl.module -@BetaInteropApi actual val platformModule = module { single { SqlDelightDriverFactory().createDriver() } @@ -27,11 +28,15 @@ actual val platformModule = module { Darwin.create {} } - single { + single { IOSNotificationService( - log = getWith("IOSNotificationService") + log = getWith("IOSNotificationService"), + syncService = get(), ) } + single { + get() + } val baseKermit = Logger(config = StaticConfig(logWriterList = listOf(NSLogWriter())), tag = "Droidcon") factory { (tag: String?) -> if (tag != null) baseKermit.withTag(tag) else baseKermit } @@ -68,3 +73,12 @@ fun Koin.get(objCProtocol: ObjCProtocol, qualifier: Qualifier?): Any { val kClazz = requireNotNull(getOriginalKotlinClass(objCProtocol)) { "Could not get original kotlin class for $objCProtocol." } return get(kClazz, qualifier, null) } + +fun Koin.getAny(objCObject: ObjCObject, qualifier: Qualifier?, parameters: ParametersDefinition?): Any { + val kclass = when (objCObject) { + is ObjCClass -> getOriginalKotlinClass(objCObject) + is ObjCProtocol -> getOriginalKotlinClass(objCObject) + else -> null + } ?: error("Couldn't resolve Kotlin Class for $objCObject") + return get(kclass, qualifier, parameters) +} diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt index e36ec8769..782d5ef11 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt @@ -2,10 +2,10 @@ package co.touchlab.droidcon import co.touchlab.droidcon.util.printThrowable import co.touchlab.kermit.Logger +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import kotlin.coroutines.CoroutineContext class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : CoroutineScope { diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.ios.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.ios.kt index 79b4eaa9a..01c4a01c6 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.ios.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightDriverFactory.ios.kt @@ -5,7 +5,5 @@ import app.cash.sqldelight.driver.native.NativeSqliteDriver import co.touchlab.droidcon.db.DroidconDatabase actual class SqlDelightDriverFactory { - actual fun createDriver(): SqlDriver { - return NativeSqliteDriver(DroidconDatabase.Schema, "droidcon2023.db") - } + actual fun createDriver(): SqlDriver = NativeSqliteDriver(DroidconDatabase.Schema, "droidcon2024.db") } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt index b89746960..92058b876 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt @@ -1,10 +1,11 @@ package co.touchlab.droidcon.service +import co.touchlab.droidcon.application.service.Notification import co.touchlab.droidcon.application.service.NotificationService import co.touchlab.droidcon.domain.entity.Session +import co.touchlab.droidcon.domain.service.SyncService import co.touchlab.droidcon.util.wrapMultiThreadCallback import co.touchlab.kermit.Logger -import com.russhwolf.settings.ExperimentalSettingsApi import kotlinx.datetime.Instant import kotlinx.datetime.toNSDate import platform.Foundation.NSCalendar @@ -23,35 +24,15 @@ import platform.UserNotifications.UNAuthorizationStatusDenied import platform.UserNotifications.UNAuthorizationStatusNotDetermined import platform.UserNotifications.UNCalendarNotificationTrigger import platform.UserNotifications.UNMutableNotificationContent -import platform.UserNotifications.UNNotification -import platform.UserNotifications.UNNotificationPresentationOptionAlert -import platform.UserNotifications.UNNotificationPresentationOptions import platform.UserNotifications.UNNotificationRequest -import platform.UserNotifications.UNNotificationResponse import platform.UserNotifications.UNNotificationSound import platform.UserNotifications.UNUserNotificationCenter -import platform.UserNotifications.UNUserNotificationCenterDelegateProtocol -import platform.darwin.NSObject - -@OptIn( - ExperimentalUnsignedTypes::class, - ExperimentalSettingsApi::class, -) -class IOSNotificationService( - private val log: Logger, -) : NotificationService { - private val notificationCenter: UNUserNotificationCenter by lazy { - val center = UNUserNotificationCenter.currentNotificationCenter() - center.delegate = notificationDelegate - center - } - private lateinit var notificationHandler: NotificationHandler - private val notificationDelegate: UNUserNotificationCenterDelegateProtocol by lazy { - NotificationDelegate(notificationHandler) - } +class IOSNotificationService(private val log: Logger, private val syncService: SyncService) : NotificationService { + private val notificationCenter = UNUserNotificationCenter.currentNotificationCenter() + private var notificationHandler: DeepLinkNotificationHandler? = null - override fun setHandler(notificationHandler: NotificationHandler) { + override fun setHandler(notificationHandler: DeepLinkNotificationHandler) { this.notificationHandler = notificationHandler } @@ -66,25 +47,30 @@ class IOSNotificationService( when (notificationSettings.authorizationStatus) { UNAuthorizationStatusNotDetermined -> { val requestOptions = UNAuthorizationOptionAlert or UNAuthorizationOptionSound - val (isAuthorized, error) = wrapMultiThreadCallback { notificationCenter.requestAuthorizationWithOptions(requestOptions, it) } + val (isAuthorized, error) = wrapMultiThreadCallback { + notificationCenter.requestAuthorizationWithOptions(requestOptions, it) + } if (error != null) { log.i { "Notifications authorization request failed with '$error'." } } return isAuthorized } + UNAuthorizationStatusDenied -> { log.i { "Notifications not authorized." } return false } + UNAuthorizationStatusAuthorized -> { log.i { "Notifications authorized." } return true } + else -> return false } } - override suspend fun schedule(type: NotificationService.NotificationType, sessionId: Session.Id, title: String, body: String, delivery: Instant, dismiss: Instant?) { + override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { log.v { "Scheduling local notification at ${delivery.toNSDate().description}." } val deliveryDate = delivery.toNSDate() val allUnits = NSCalendarUnitSecond or @@ -102,18 +88,18 @@ class IOSNotificationService( content.setTitle(title) content.setBody(body) content.setSound(UNNotificationSound.defaultSound) - val typeString = when (type) { - NotificationService.NotificationType.Reminder -> NOTIFICATION_TYPE_REMINDER - NotificationService.NotificationType.Feedback -> NOTIFICATION_TYPE_FEEDBACK + val (typeValue, sessionId) = when (notification) { + is Notification.Local.Feedback -> Notification.Values.FEEDBACK_TYPE to notification.sessionId + is Notification.Local.Reminder -> Notification.Values.REMINDER_TYPE to notification.sessionId } content.setUserInfo( mapOf( - NOTIFICATION_SESSION_ID_KEY to sessionId.value, - NOTIFICATION_TYPE_KEY to typeString, - ) + Notification.Keys.NOTIFICATION_TYPE to typeValue, + Notification.Keys.SESSION_ID to sessionId.value, + ), ) - val request = UNNotificationRequest.requestWithIdentifier("${sessionId.value}-$typeString", content, trigger) + val request = UNNotificationRequest.requestWithIdentifier("${sessionId.value}-$typeValue", content, trigger) val error = wrapMultiThreadCallback { notificationCenter.addNotificationRequest(request, it) } if (error == null) { @@ -124,47 +110,73 @@ class IOSNotificationService( } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { return } + if (sessionIds.isEmpty()) { + return + } log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } notificationCenter.removePendingNotificationRequestsWithIdentifiers(sessionIds.map { it.value }) } - // Delegate necessary to show notification. - private class NotificationDelegate( - private val notificationHandler: NotificationHandler, - ) : NSObject(), UNUserNotificationCenterDelegateProtocol { - override fun userNotificationCenter( - center: UNUserNotificationCenter, - willPresentNotification: UNNotification, - withCompletionHandler: (UNNotificationPresentationOptions) -> Unit, - ) { - withCompletionHandler(UNNotificationPresentationOptionAlert) - } + @Suppress("unused") + suspend fun didReceiveNotificationResponse(userInfo: Map) { + val notification = userInfo.parseNotification() ?: return + + handleNotification(notification) + } + + @Suppress("unused") + suspend fun didReceiveRemoteNotification(userInfo: Map): Boolean { + val notification = userInfo.parseNotification() ?: return false + + handleNotification(notification) + return notification is Notification.Remote.RefreshData + } - override fun userNotificationCenter( - center: UNUserNotificationCenter, - didReceiveNotificationResponse: UNNotificationResponse, - withCompletionHandler: () -> Unit, - ) { - val notification = didReceiveNotificationResponse.notification - val notificationType = when (notification.request.content.userInfo[NOTIFICATION_TYPE_KEY] as String) { - NOTIFICATION_TYPE_REMINDER -> NotificationService.NotificationType.Reminder - NOTIFICATION_TYPE_FEEDBACK -> NotificationService.NotificationType.Feedback - else -> null + private suspend fun handleNotification(notification: Notification) { + when (notification) { + is Notification.DeepLink -> { + val notificationHandler = notificationHandler + if (notificationHandler != null) { + notificationHandler.handleDeepLinkNotification(notification) + } else { + log.w { "notificationHandler not registered when received $notification" } + } } - if (notificationType != null) { - val sessionId = notification.request.content.userInfo[NOTIFICATION_SESSION_ID_KEY] as String - notificationHandler.notificationReceived(sessionId, notificationType) + + Notification.Remote.RefreshData -> syncService.forceSynchronize() + } + } + + private fun Map.parseNotification(): Notification? = + when (val typeValue = this[Notification.Keys.NOTIFICATION_TYPE] as? String) { + Notification.Values.REMINDER_TYPE -> this.parseReminderNotification() + Notification.Values.FEEDBACK_TYPE -> this.parseFeedbackNotification() + Notification.Values.REFRESH_DATA_TYPE -> Notification.Remote.RefreshData + else -> { + log.e { "Unknown notification type <$typeValue>, ignoring." } + null } - withCompletionHandler() } + + private fun Map.parseReminderNotification(): Notification.Local.Reminder? { + val sessionId = this[Notification.Keys.SESSION_ID] as? String ?: run { + log.e { "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." } + return null + } + + return Notification.Local.Reminder( + sessionId = Session.Id(sessionId), + ) } - companion object { - private const val NOTIFICATION_SESSION_ID_KEY = "NOTIFICATION_SESSION_ID_KEY" + private fun Map.parseFeedbackNotification(): Notification.Local.Feedback? { + val sessionId = this[Notification.Keys.SESSION_ID] as? String ?: run { + log.e { "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." } + return null + } - private const val NOTIFICATION_TYPE_KEY = "NOTIFICATION_TYPE_KEY" - private const val NOTIFICATION_TYPE_REMINDER = "NOTIFICATION_TYPE_REMINDER" - private const val NOTIFICATION_TYPE_FEEDBACK = "NOTIFICATION_TYPE_FEEDBACK" + return Notification.Local.Feedback( + sessionId = Session.Id(sessionId), + ) } } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt index bbff7c727..2b94136a8 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt @@ -17,23 +17,25 @@ import platform.darwin.NSObjectMeta @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) @BetaInteropApi -class BundleResourceReader( - private val bundle: NSBundle = NSBundle.bundleForClass(BundleMarker) -) : ResourceReader { +class BundleResourceReader(private val bundle: NSBundle = NSBundle.bundleForClass(BundleMarker)) : ResourceReader { override fun readResource(name: String): String { // TODO: Catch iOS-only exceptions and map them to common ones. val (filename, type) = when (val lastPeriodIndex = name.lastIndexOf('.')) { 0 -> { null to name.drop(1) } + in 1..Int.MAX_VALUE -> { name.take(lastPeriodIndex) to name.drop(lastPeriodIndex + 1) } + else -> { name to null } } - val path = bundle.pathForResource(filename, type) ?: error("Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})") + val path = bundle.pathForResource(filename, type) ?: error( + "Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})", + ) return memScoped { val errorPtr = alloc>() diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/WrapMultiThreadCallback.kt similarity index 72% rename from shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt rename to shared/src/iosMain/kotlin/co/touchlab/droidcon/util/WrapMultiThreadCallback.kt index 253eca8fc..a313cf03a 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/WrapMultiThreadCallback.kt @@ -1,19 +1,18 @@ package co.touchlab.droidcon.util -import co.touchlab.stately.freeze import kotlinx.coroutines.CompletableDeferred // Closures crash because of multi-thread execution, these methods prevent that. suspend fun wrapMultiThreadCallback(call: (callback: (T) -> Unit) -> Unit): T { - val completable = CompletableDeferred().freeze() + val completable = CompletableDeferred() val closure: (T) -> Unit = { completable.complete(it) } - call(closure.freeze()) + call(closure) return completable.await() } suspend fun wrapMultiThreadCallback(call: (callback: (T1, T2) -> Unit) -> Unit): Pair { - val completable = CompletableDeferred>().freeze() + val completable = CompletableDeferred>() val closure: (T1, T2) -> Unit = { t1, t2 -> completable.complete(t1 to t2) } - call(closure.freeze()) + call(closure) return completable.await() }