Slide 1

Slide 1 text

What’s new in Jetpack #androidjetpack

Slide 2

Slide 2 text

android jetpack

Slide 3

Slide 3 text

79% 2021 using 2+ non-core Jetpack libraries 90% 2022 of top 1K apps

Slide 4

Slide 4 text

Room Lifecycle Activity DataStore Browser JankStats Baseline Profiles Macrobenchmark Tracing WindowManager DragAndDrop AppCompat Compose Annotation Fragment Media Core Navigation WorkManager SlidingPaneLayout SavedState RecyclerView Preference LocalBroadcastManager Paging Emoji2 CustomView ConstraintLayout Camera Collection

Slide 5

Slide 5 text

Unintentional dependency changes Android 12 Target

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Link: https://developer.android.com/distribute/play-policies

Slide 8

Slide 8 text

Link: https://developer.android.com/google/play/requirements/target-sdk

Slide 9

Slide 9 text

Link: https://developer.android.com/guide/components/intents-filters#DeclareMutabilityPendingIntent

Slide 10

Slide 10 text

// Application Codes PendingIntent.getBroadcast( context, REQUEST_CODE, intent, - flags + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags or PendingIntent.FLAG_IMMUTABLE + } else { + flags + } )

Slide 11

Slide 11 text

Adobe Stock#243026154 play-services- analytics 17.0.1 WorkManager 2.7 https://developers.google.com/android/guides/releases#august_19_2021 https://developer.android.com/jetpack/androidx/releases/work#version_27_2 Library Updates

Slide 12

Slide 12 text

// androidx.work:work-runtime:2.7.0 // Unintentional dependency changes!!! androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0 androidx.core:core:1.5.0 -> 1.6.0 + androidx.startup:startup-runtime:1.0.0 + \--- androidx.tracing:tracing:1.0.0 androidx.room:room-runtime:2.1.0 -> 2.2.5 org.jetbrains.kotlin:kotlin-stdlib:1.4.32 -> 1.5.30 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32 -> 1.5.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3 -> 1.5.0

Slide 13

Slide 13 text

// androidx.work:work-runtime:2.7.0 // Unintentional dependency changes!!! androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0 androidx.core:core:1.5.0 -> 1.6.0 + androidx.startup:startup-runtime:1.0.0 + \--- androidx.tracing:tracing:1.0.0 androidx.room:room-runtime:2.1.0 -> 2.2.5 org.jetbrains.kotlin:kotlin-stdlib:1.4.32 -> 1.5.30 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32 -> 1.5.0 org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3 -> 1.5.0 How to prevent this? 🤔

Slide 14

Slide 14 text

Link: https://github.com/dropbox/dependency-guard

Slide 15

Slide 15 text

// Example dependencies { - implementation "androidx.activity:activity-ktx:1.3.1" + implementation "androidx.activity:activity-ktx:1.4.0" }

Slide 16

Slide 16 text

// Example dependencies { - implementation "androidx.activity:activity-ktx:1.3.1" + implementation "androidx.activity:activity-ktx:1.4.0" } ----------------------------------------------------------------------- $ ./gradlew dependencyGuard > Task :app:dependencyGuard FAILED Dependencies Changed in :app for configuration releaseRuntimeClasspath - androidx.activity:activity-ktx:1.3.1 + androidx.activity:activity-ktx:1.4.0 - androidx.activity:activity:1.3.1 + androidx.activity:activity:1.4.0

Slide 17

Slide 17 text

Rewritten from Java to Kotlin Kotlin

Slide 18

Slide 18 text

18 Support for Kotlin! Google I/O '17 Link: https://android-developers.googleblog.com/2017/05/android-announces-support-for-kotlin.html

Slide 19

Slide 19 text

2011 Android Support Library 2017 Architecture Components Kotlin Timeline ... Link: https://android-developers.googleblog.com/2017/11/announcing-architecture-components-10.html package helloWorld fun main(args: Array) { println("Hello World!") }

Slide 20

Slide 20 text

2011 Android Support Library 2017 Architecture Components Kotlin 2018 Android Jetpack KTX Timeline ... Link: https://android-developers.googleblog.com/2018/02/introducing-android-ktx-even-sweeter.html // Kotlin val uri = Uri.parse(myUriString) // Kotlin with Android KTX val uri = myUriString.toUri()

Slide 21

Slide 21 text

2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin first! Benchmark 2018 Android Jetpack KTX Timeline ... Link: https://android-developers.googleblog.com/2019/05/google-io-2019-empowering-developers.html Kotlin/Everywhere Many new Jetpack APIs and features will be offered first in Kotlin.

Slide 22

Slide 22 text

2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline ... Link: https://android-developers.googleblog.com/2020/07/getting-on-same-page-with-paging-3.html

Slide 23

Slide 23 text

2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline ... Link: https://developer.android.com/jetpack/androidx/releases/navigation#2.4.0 2022 Collection 1.3 Annotation 1.4 Navigation 2.4 Fragment 1.5 Activity 1.4 Partially rewritten.

Slide 24

Slide 24 text

// Navigation.java public final class Navigation { @NonNull public static View.OnClickListener createNavigateOnClickListener( @IdRes final int resId) { return createNavigateOnClickListener(resId, null); } @NonNull public static View.OnClickListener createNavigateOnClickListener( @IdRes final int resId, @Nullable final Bundle args) { return new View.OnClickListener() { @Override public void onClick(View view) { findNavController(view).navigate(resId, args); } }; } }

Slide 25

Slide 25 text

// Navigation.kt public object Navigation { @JvmStatic @JvmOverloads public fun createNavigateOnClickListener( @IdRes resId: Int, args: Bundle? = null ): View.OnClickListener { return View.OnClickListener { view -> findNavController(view).navigate(resId, args) } } }

Slide 26

Slide 26 text

// api/current.txt public final class Navigation { - method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int); - method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int, android.os.Bundle?); + method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId); + method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args); + field public static final androidx.navigation.Navigation INSTANCE; } Same APIs! -ktx is no longer required.

Slide 27

Slide 27 text

2011 Android Support Library 2017 Architecture Components Kotlin 2019 Kotlin first! Benchmark 2018 Android Jetpack KTX 2020 Compose DataStore Window Paging 3 Timeline 2022 Collection 1.3 Annotation 1.4 Navigation 2.4 Fragment 1.5 Activity 1.4 2023 (?) SavedState 1.2 Room 2.5 Work 2.8 ... ...

Slide 28

Slide 28 text

// WorkManager // 2.7.1 val constraints = Constraints.Builder() .setRequiresCharging(true) .setRequiresBatteryNotLow(true) .setRequiresDeviceIdle(true) .build() // 2.8.0-alpha02 val constraints = Constraints( requiresCharging = true, requiresBatteryNotLow = true, requiresDeviceIdle = true, ) Enhanced APIs for Kotlin users!

Slide 29

Slide 29 text

Room, Navigation, and more… Architecture Components

Slide 30

Slide 30 text

Room Link: https://developer.android.com/jetpack/androidx/releases/room#2.4.0 Stable Auto Migrations Relational Query Methods Native support for Paging 3.0 (room-paging) Performance improvements with KSP androidx.room:room:2.4.2

Slide 31

Slide 31 text

Link: https://developer.android.com/training/data-storage/room/migrating-db-versions#automated // Auto Migrations @Database( version = 3, entities = [User::class], autoMigrations = [ AutoMigration(from = 1, to = 2, spec = AppDatabase.MyAutoMigration::class), AutoMigration(from = 2, to = 3) ] ) abstract class AppDatabase : RoomDatabase() { @RenameTable(fromTableName = "User", toTableName = "AppUser") class MyAutoMigration : AutoMigrationSpec ... }

Slide 32

Slide 32 text

Link: https://developer.android.com/training/data-storage/room/accessing-data#multimap // Supports multimap return types // like Map, SparseArray, LongSparseArray, etc. // 1:1 Relation Map @Query("SELECT * FROM Song JOIN Artist ON Song.artistId = Artist.artistId") fun getSongAndArtist(): Map // 1:N Relation Map @Query("SELECT * FROM Artist JOIN Album ON Artist.id = Album.artistId") fun getArtistAndAlbums(): Map>

Slide 33

Slide 33 text

Paging Link: https://developer.android.com/jetpack/androidx/releases/paging#3.1.0 Stable Stable support for Rx and Guava integrations Improvements to handling of invalid or stale data More comprehensive callbacks New introductory codelab for Paging 3 androidx.paging:paging:3.1.1

Slide 34

Slide 34 text

DataStore Link: https://developer.android.com/jetpack/androidx/releases/datastore#1.0.0 Stable MAD Skills: DataStore goo.gle/mad-skills-datastore How to migrate from SharedPreferences to Proto DataStore. d.android.com/codelabs/android-proto-datastore#7 androidx.datastore:datastore:1.0.0

Slide 35

Slide 35 text

Lifecycle Deprecated @OnLifecycleEvent Integration between ViewModel and Jetpack Compose Restartable Lifecycle-aware coroutines androidx.lifecycle:lifecycle:2.4.1 androidx.lifecycle:lifecycle:2.5.0+ ViewModel CreationExtras Link: https://developer.android.com/jetpack/androidx/releases/lifecycle Stable / RC

Slide 36

Slide 36 text

Link: https://developer.android.com/training/data-storage/room/accessing-data#multimap // Use DefaultLifecycleObserver or LifecycleEventObserver instead. - class MyObserver(...) : LifecycleObserver { + class MyObserver(...) : DefaultLifecycleObserver { - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { ... } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { ... } } Stable

Slide 37

Slide 37 text

Link: https://developer.android.com/topic/libraries/architecture/coroutines#restart // Restartable Lifecycle-aware coroutines viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.someDataFlow.collect { // Process item } } } viewLifecycleOwner.lifecycleScope.launch { exampleProvider.exampleFlow() .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) .collect { // Process the value. } } Stable

Slide 38

Slide 38 text

// current class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels() ... } class MyViewModel( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { fun loadData(id: String) { ... } } RC

Slide 39

Slide 39 text

// current class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels() override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory { return super.getDefaultViewModelProviderFactory() // SavedStateViewModelFactory } } class MyViewModel( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { fun loadData(id: String) { ... } } RC

Slide 40

Slide 40 text

class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels() } class MyViewModel( private val savedStateHandle: SavedStateHandle, + private val id: String, ) : ViewModel() { - fun loadData(id: String) { - ... - } } RC

Slide 41

Slide 41 text

Link: https://pluu.github.io/blog/android/2022/03/12/creationextras/ class MyActivity : AppCompatActivity() { + private val key = object : CreationExtras.Key {} private val factory = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create( modelClass: Class, + extras: CreationExtras, ): T { val savedStateHandle = extras.createSavedStateHandle() + val id = extras[key].orEmpty() return MainViewModel( savedStateHandle = savedStateHandle, + id = id, ) as T } } override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory = factory ... RC

Slide 42

Slide 42 text

Navigation Link: https://d.android.com/jetpack/androidx/releases/navigation#2.4.0 Stable Support for two-pane layouts Integration with Jetpack Compose Support for Multiple Back Stacks androidx.navigation:navigation:2.4.2

Slide 43

Slide 43 text

Phone Tablet Link: https://github.com/CesarValiente/navigation-foldable

Slide 44

Slide 44 text

Link: https://github.com/CesarValiente/navigation-foldable class NavigationActivity : AppCompatActivity(R.layout.activity_navigation)

Slide 45

Slide 45 text

Link: https://github.com/CesarValiente/navigation-foldable class TwoPaneFragment : AbstractListDetailFragment() { override fun onCreateListPaneView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { return ListPaneBinding.inflate(inflater, container, false) } override fun onListPaneViewCreated(view: View, savedInstanceState: Bundle?) { oneFragmentButton.setOnClickListener { openDetails(R.id.one_fragment) } } ... }

Slide 46

Slide 46 text

Link: https://github.com/CesarValiente/navigation-foldable class TwoPaneFragment : AbstractListDetailFragment() { ... private fun openDetails(destinationId: Int) { val detailNavController = detailPaneNavHostFragment.navController detailNavController.navigate( resId = destinationId, args = null, navOptions = navOptions { popUpTo(detailNavController.graph.startDestinationId) { inclusive = true } } ) slidingPaneLayout.open() } override fun onCreateDetailPaneNavHostFragment(): NavHostFragment { return NavHostFragment.create(R.navigation.detail_pane_nav_graph) } }

Slide 47

Slide 47 text

Link: https://github.com/CesarValiente/navigation-foldable class OneFragment : Fragment(R.layout.one_fragment) class TwoFragment : Fragment(R.layout.two_fragment) class ThreeFragment : Fragment(R.layout.three_fragment)

Slide 48

Slide 48 text

Large Screen, and more... User Interface

Slide 49

Slide 49 text

SlidingPaneLayout Link: https://d.android.com/jetpack/androidx/releases/slidingpanelayout#1.2.0 Stable Support for two-pane UX androidx.slidingpanelayout: slidingpanelayout:1.2.0

Slide 50

Slide 50 text

Link: https://developer.android.com/guide/topics/ui/layout/twopane

Slide 51

Slide 51 text

Link: https://developer.android.com/guide/topics/ui/layout/twopane

Slide 52

Slide 52 text

Link: https://developer.android.com/guide/topics/ui/layout/twopane // A method on the Fragment that owns the SlidingPaneLayout, // called by the adapter when an item is selected. fun openDetails(itemId: Int) { childFragmentManager.commit { setReorderingAllowed(true) replace(R.id.detail_container, bundleOf("itemId" to itemId)) // If we're already open and the detail pane is visible, // crossfade between the fragments. if (binding.slidingPaneLayout.isOpen) { setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) } } binding.slidingPaneLayout.open() }

Slide 53

Slide 53 text

WindowManager Link: https://developer.android.com/jetpack/androidx/releases/window#1.0.0 Stable Supports foldable phones to adjust how content is displayed Smart layout avoids placing content in occluded areas Already integrated into SlidingPaneLayout androidx.window:window:1.0.0

Slide 54

Slide 54 text

Preference Link: https://d.android.com/jetpack/androidx/releases/preference#1.2.0 Stable Support for two-pane preference androidx.preference:preference:1.2.0

Slide 55

Slide 55 text

Phone Tablet Link: https://github.com/CesarValiente/preferences-foldable

Slide 56

Slide 56 text

Link: https://github.com/CesarValiente/preferences-foldable class PreferenceActivity : AppCompatActivity(R.layout.activity_preference) class TwoPanePreference : PreferenceHeaderFragmentCompat() { override fun onCreatePreferenceHeader(): PreferenceFragmentCompat { return HeaderPreference() } } class HeaderPreference : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) } }

Slide 57

Slide 57 text

Link: https://github.com/CesarValiente/preferences-foldable

Slide 58

Slide 58 text

DragAndDrop Link: https://developer.android.com/jetpack/androidx/releases/draganddrop#1.0.0 Stable Simplifies ingesting dropped data Drop target highlighting androidx.draganddrop:draganddrop:1.0.0

Slide 59

Slide 59 text

Link: https://developer.android.com/guide/topics/ui/drag-drop

Slide 60

Slide 60 text

Link: https://github.com/android/user-interface-samples/tree/main/DragAndDrop DragStartHelper(binding.textDragItem) { view, _ -> val text = (view as TextView).text // Create the ClipData to be shared val dragClipData = ClipData.newPlainText(/*label*/"Text", text) // Use the default drag shadow val dragShadowBuilder = View.DragShadowBuilder(view) // Initiate the drag. Note the DRAG_FLAG_GLOBAL, // which allows for drag events to be listened to by apps other than the source app. view.startDragAndDrop(dragClipData, dragShadowBuilder, null, DRAG_FLAG_GLOBAL) }.attach()

Slide 61

Slide 61 text

Link: https://github.com/android/user-interface-samples/tree/main/DragAndDrop DropHelper.configureView( /* activity */ this, binding.textDropTarget, /* mimeTypes */ arrayOf( ClipDescription.MIMETYPE_TEXT_PLAIN, "application/x-arc-uri-list" // Support external items on Chrome OS Android 9 ), DropHelper.Options.Builder() .setHighlightColor(getColor(R.color.purple_300)) .build() ) { view, payload -> resetDropTarget() val item = payload.clip.getItemAt(0) val (_, remaining) = payload.partition { it == item } if (payload.clip.description.hasMimeType(MIMETYPE_TEXT_PLAIN) { binding.textDropTarget.text = item.text } // Allow the system to handle any remaining ClipData.Item objects if applicable remaining }

Slide 62

Slide 62 text

Fragment Stable New state manager only Support multiple back stacks Support FragmentStrictMode androidx.fragment:fragment:1.4.1

Slide 63

Slide 63 text

Link: https://medium.com/androiddevelopers/fragments-rebuilding-the-internals-61913f8bf48e Old New // Fragment 1.3 FragmentManager.enableNewStateManager(false) // Fragment 1.4 FragmentManager.enableNewStateManager(false)

Slide 64

Slide 64 text

Link: https://developer.android.com/guide/fragments/debugging FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder() .detectFragmentReuse() .detectFragmentTagUsage() .detectRetainInstanceUsage() .detectSetUserVisibleHint() .detectTargetFragmentUsage() .detectWrongFragmentContainer() .allowViolation(MyFragment::class.java, SetUserVisibleHintViolation::class.java) .apply { if (BuildConfig.DEBUG) { // Fail early on DEBUG builds penaltyDeath() } else { // Log to Crashlytics on RELEASE builds penaltyListener { FirebaseCrashlytics.getInstance().recordException(it) } } } .build()

Slide 65

Slide 65 text

AppCompat Stable / Alpha Emoji2 integration androidx.appcompat:appcompat:1.4.2 androidx.appcompat:appcompat:1.6.0+ Support for custom app locales 🫠🧑🦰🦩 ☐☐☐ Link: https://developer.android.com/guide/topics/ui/look-and-feel/emoji2

Slide 66

Slide 66 text

// Get app locales val locales: LocaleListCompat = AppCompatDelegate.getApplicationLocales() // Set app locales val locales: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY") // or LocaleListCompat.create(Locale.KOREA) AppCompatDelegate.setApplicationLocales(locales) Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl

Slide 67

Slide 67 text

override fun attachBaseContext(newBase: Context) { if (SDK_INT < 33) { // Preferred locales are managed automatically on API level 33+, // but we need to load them manually on earlier platform versions. val locales = loadLocalesFromPreferences() AppCompatDelegate.setApplicationLocales(locales) } super.attachBaseContext(newBase) } Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl

Slide 68

Slide 68 text

override fun attachBaseContext(newBase: Context) { if (SDK_INT < 33) { // Preferred locales are managed automatically on API level 33+, // but we need to load them manually on earlier platform versions. val locales = loadLocalesFromPreferences() AppCompatDelegate.setApplicationLocales(locales) } super.attachBaseContext(newBase) } Link: https://developer.android.com/about/versions/13/features/app-languages#androidx-impl

Slide 69

Slide 69 text

ConstraintLayout Alpha Grid helper androidx.constraintlayout:constraintlayout:2.2.0+ Link: https://github.com/androidx/constraintlayout/wiki/What's-New-in-2.2

Slide 70

Slide 70 text

... ...

Slide 71

Slide 71 text

Slide 72

Slide 72 text

9 8 10 11 13 12 14 15 17 16 18 19 21 20 22 23 25 24 26 27 5 4 6 7 1 0 2 3

Slide 73

Slide 73 text

5 4 6 7 1 0 2 3

Slide 74

Slide 74 text

25 24

Slide 75

Slide 75 text

Now in 1.2 beta! Compose

Slide 76

Slide 76 text

Nested Scrolling Interop Link: https://d.android.com/jetpack/compose/interop/compose-in-existing-ui#nested-scrolling Compose 1.2 androidx.compose.ui:ui:1.2.0-alpha08 + Lazy Layouts Scrolling View Compose AppBar Layout

Slide 77

Slide 77 text

Scrolling Compose in View Lazy Layouts AppBar Layout

Slide 78

Slide 78 text

Link: Nested Scrolling Interop Phase 1: Scrolling compose in cooperating view (I5d1ac)

Slide 79

Slide 79 text

@ExperimentalComposeUiApi class ComposeInAndroidCoordinatorLayout : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.compose_in_android_coordinator_layout) findViewById(R.id.compose_view).apply { setContent { val nestedScrollInterop = rememberNestedScrollInteropConnection(this) LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) { items(20) { item -> Box(...) { Text(item.toString()) } } } } } } } Link: Nested Scrolling Interop Phase 1: Scrolling compose in cooperating view (I5d1ac)

Slide 80

Slide 80 text

Scrolling View in Compose Scrolling View Compose

Slide 81

Slide 81 text

Slide 82

Slide 82 text

val ToolbarHeight = 48.dp @Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() } val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y val newOffset = toolbarOffsetHeightPx.value + delta toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) return Offset.Zero } } } ... Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)

Slide 83

Slide 83 text

@Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { ... Box( Modifier .fillMaxSize() .nestedScroll(nestedScrollConnection) ) { TopAppBar( modifier = Modifier .height(ToolbarHeight) .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }, title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") } ) ... Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)

Slide 84

Slide 84 text

@Composable private fun NestedScrollInteropComposeParentWithAndroidChild() { ... AndroidView( { context -> LayoutInflater.from(context) .inflate(R.layout.android_in_compose_nested_scroll_interop, null).apply { with(findViewById(R.id.main_list)) { layoutManager = LinearLayoutManager(context, VERTICAL, false) adapter = NestedScrollInteropAdapter() } }.also { ViewCompat.setNestedScrollingEnabled(it, true) } }, modifier = Modifier.padding(top = ToolbarHeight).fillMaxWidth(), ) } } Link: Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent (If7949)

Slide 85

Slide 85 text

Compose in RecyclerView Link: https://d.android.com/jetpack/compose/interop/compose-in-existing-ui#compose-recyclerview Compose 1.2 androidx.compose.ui:ui:1.2.0-beta02 + androidx.recyclerview: recyclerview:1.3.0-alpha02 +

Slide 86

Slide 86 text

// Compose 1.1 + RecyclerView 1.2 import androidx.compose.ui.platform.ComposeView class MyComposeAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(...): MyComposeViewHolder { return MyComposeViewHolder(ComposeView(parent.context)) } override fun onViewRecycled(holder: MyComposeViewHolder) { + holder.composeView.disposeComposition() } } class MyComposeViewHolder(val composeView: ComposeView) : RecyclerView.ViewHolder(composeView) { init { + composeView.setViewCompositionStrategy( + ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed + ) } } Link: https://developer.android.com/jetpack/compose/interop/compose-in-existing-ui#compose-recyclerview

Slide 87

Slide 87 text

// Compose 1.2 + RecyclerView 1.3 import androidx.compose.ui.platform.ComposeView class MyComposeAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(...): MyComposeViewHolder { return MyComposeViewHolder(ComposeView(parent.context)) } // override fun onViewRecycled(holder: MyComposeViewHolder) { // holder.composeView.disposeComposition() // } } class MyComposeViewHolder(val composeView: ComposeView) : RecyclerView.ViewHolder(composeView) { // init { // composeView.setViewCompositionStrategy( // ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed // ) // } }

Slide 88

Slide 88 text

CustomView-PoolingContainer Link: https://cs.android.com/.../androidx-customview-poolingcontainer-documentation.md

Slide 89

Slide 89 text

abstract class AbstractComposeView(...) : ViewGroup(...) { private var disposeViewCompositionStrategy: (() -> Unit)? = - ViewCompositionStrategy.DisposeOnDetachedFromWindow.installFor(this) + ViewCompositionStrategy.Default.installFor(this) } class ComposeView(...) : AbstractComposeView(...) { ... } interface ViewCompositionStrategy { companion object { + val Default: ViewCompositionStrategy + get() = DisposeOnDetachedFromWindowIfNotInPoolingContainer } ... } Link: Integrate PoolingContainer into Compose UI(If7282)

Slide 90

Slide 90 text

+ object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view: AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)

Slide 91

Slide 91 text

+ object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view: AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)

Slide 92

Slide 92 text

+ object DisposeOnDetachedFromWindowIfNotInPoolingContainer : ViewCompositionStrategy { + override fun installFor(view: AbstractComposeView): () -> Unit { + val listener = object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) {} + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + val poolingContainerListener = PoolingContainerListener { view.disposeComposition() } + view.addPoolingContainerListener(poolingContainerListener) + return { + view.removeOnAttachStateChangeListener(listener) + view.removePoolingContainerListener(poolingContainerListener) + } + } + } Link: Integrate PoolingContainer into Compose UI(If7282)

Slide 93

Slide 93 text

public class RecyclerView extends ViewGroup implements ... { public RecyclerView(...) { ... + PoolingContainer.setPoolingContainer(this, true); } public final class Recycler { ... void recycleViewHolderInternal(ViewHolder holder) { ... mViewInfoStore.removeViewHolder(holder); if (!cached && !recycled && transientStatePreventsRecycling) { + PoolingContainer.callPoolingContainerOnRelease(holder.itemView); ... } } } } Link: Integrate PoolingContainer into RecyclerView(Ib89d2)

Slide 94

Slide 94 text

Link: https://cs.android.com/.../androidx-main:customview/customview-poolingcontainer/.../PoolingContainer.kt See the code for details. 👀

Slide 95

Slide 95 text

Lazy Layouts Link: https://developer.android.com/jetpack/compose/lists Compose 1.2 androidx.compose.foundation:foundation Lazy Layouts in Compose

Slide 96

Slide 96 text

@OptIn(ExperimentalFoundationApi::class) LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arragement.spaceBy(16.dp), horizontalArrangement = Arragement.spaceBy(16.dp), ) { items(data) { item -> Item(item) } }

Slide 97

Slide 97 text

// New! LazyHorizontalGrid( rows = GridCells.Fixed(2), verticalArrangement = Arragement.spaceBy(16.dp), horizontalArrangement = Arragement.spaceBy(16.dp), ) { items(data) { item -> Item(item) } }

Slide 98

Slide 98 text

instead of androidx.recyclerview.widget. StaggeredGridLayoutManager Not Available Staggered Grids Link: https://issuetracker.google.com/issues/182882362

Slide 99

Slide 99 text

Adobe Stock#243026154 Downloadable Fonts Link: https://developer.android.com/jetpack/compose/text#downloadable-fonts Compose 1.2 androidx.compose.ui:ui-text-google-fonts

Slide 100

Slide 100 text

val provider = GoogleFont.Provider( providerAuthority = "com.google.android.gms.fonts", providerPackage = "com.google.android.gms", certificates = R.array.com_google_android_gms_fonts_certs ) val MontserratFont = GoogleFont(name = "Montserrat") val MontserratFontFamily = FontFamily( Font(googleFont = MontserratFont, fontProvider = provider) ) Text( fontFamily = MontserratFontFamily, text = "Hello World!" ) Link: https://github.com/android/compose-samples/pull/787

Slide 101

Slide 101 text

Text Magnifier Link: I30b38, b/139320979 Compose 1.2 androidx.compose.foundation:foundation

Slide 102

Slide 102 text

Show Layout Bounds immediately Link: I843d5, b/225937688 Compose 1.2 androidx.compose.ui:ui:1.2.0-beta03

Slide 103

Slide 103 text

Recommended! Performance best practices for Jetpack Compose

Slide 104

Slide 104 text

Compose for Wear OS Jetpack Compose for Wear OS Now in beta! Link: g.co/wear/compose

Slide 105

Slide 105 text

Navigation androidx.wear.compose.navigation instead of androidx.navigation:navigation-compose Compose for Wear OS val navController = rememeberSwipeDismissableNavController() SwipeDismissableNavHost( navController = navController, startDestination = Screen.MainScreen.route ) { composable(route = Screen.MainScreen.route) { MyListScreen(...) } ... }

Slide 106

Slide 106 text

ScalingLazyColumn androidx.wear.compose.material instead of androidx.compose.material:material Compose for Wear OS val scalingLazyListState = rememberScalingLazyListState() ScalingLazyColumn( state = scalingLazyListState, autoCentering = AutoCenteringParams() ) { item { ListHeader { Text("Title") } } items(messages.size) { message -> Card(/*...*/) { /*...*/ } } }

Slide 107

Slide 107 text

androidx.wear.compose.material Compose for Wear OS Picker Stepper InlineSlider

Slide 108

Slide 108 text

androidx.wear.compose.material Compose for Wear OS Dialog Circular ProgressIndicator Horizontal PageIndicator

Slide 109

Slide 109 text

Horologist Wear OS ѐߊ੗ܳ ࠁ৮ೞח Ѫਸ ݾ಴۽ ೞח ۄ੉࠳۞ܻ ޘ਺ੑפ׮. ѐߊ੗о ੌ߈੸ਵ۽ ೙ਃ۽ ೞ૑݅ 
 ই૒ ࢎਊೡ ࣻ হח ӝמਸ ઁҕ೤פ׮. • Media • Composables • Compose Layout • Audio and UI • Tiles Compose for Wear OS Link: https://github.com/google/horologist

Slide 110

Slide 110 text

Health Services Link: https://developer.android.com/training/wearables/health-services Now in alpha! Developing Health and Fitness Apps on Android

Slide 111

Slide 111 text

Jetpackਸ ѐࢶೞب۾, ੉गܳ ١۾೧઱ࣁਃ. 👆

Slide 112

Slide 112 text

э਷ ੉गо ੉޷ ١۾غয ੓ਵݶ, Starܳ ׂ۞઱ࣁਃ. 👆

Slide 113

Slide 113 text

Thank you! 
 Android Developer / NAVER WEBTOON Android GDE 
 @fornewid d.android.com/jetpack speakerdeck.com/fornewid/ whats-new-in-jetpack-2022 Resources Sungyong An