From d9868f3b49b5a4fb163ba35e4709f636d977bd3e Mon Sep 17 00:00:00 2001 From: Erik Rodriguez Date: Tue, 25 Aug 2026 00:09:47 -0500 Subject: [PATCH 1/2] Add Kotlin Parcelize snippets Snippets for the Parcelize guide (developer.android.com/kotlin/parcelize), extracted into kotlin/src/main/kotlin/com/example/android/basics with region tags, plus the parcelize plugin in kotlin/build.gradle.kts. One deviation from the page as published: the data class serializer example uses a declaration-level @OptIn rather than the page's file-level @file:OptIn. A file annotation must precede the package declaration, so it cannot sit inside a region tag, and the published snippet would otherwise lose the opt-in entirely. The page's block needs the matching change. Kotlin only. Co-authored-by: Katherine Kuan <843428+kkuan2011@users.noreply.github.com> --- kotlin/build.gradle.kts | 2 + .../com/example/android/basics/Parcelize.kt | 205 ++++++++++++++++++ .../android/basics/ParcelizeDataClass.kt | 34 +++ 3 files changed, 241 insertions(+) create mode 100644 kotlin/src/main/kotlin/com/example/android/basics/Parcelize.kt create mode 100644 kotlin/src/main/kotlin/com/example/android/basics/ParcelizeDataClass.kt diff --git a/kotlin/build.gradle.kts b/kotlin/build.gradle.kts index a4eee3ec9..60acf2c18 100644 --- a/kotlin/build.gradle.kts +++ b/kotlin/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.parcelize) } kotlin { @@ -26,6 +27,7 @@ android { getByName("release") { isMinifyEnabled = true + isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } diff --git a/kotlin/src/main/kotlin/com/example/android/basics/Parcelize.kt b/kotlin/src/main/kotlin/com/example/android/basics/Parcelize.kt new file mode 100644 index 000000000..7fcecbdd6 --- /dev/null +++ b/kotlin/src/main/kotlin/com/example/android/basics/Parcelize.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.basics + +import android.os.Parcel +import android.os.Parcelable +import kotlinx.parcelize.DataClass +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parceler +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue +import kotlinx.parcelize.TypeParceler +import kotlinx.parcelize.WriteWith +import kotlinx.parcelize.parcelableCreator + +private object BasicSnippet { + // [START android_kotlin_parcelize_basic] + // import kotlinx.parcelize.Parcelize + + @Parcelize + class User(val firstName: String, val lastName: String, val age: Int) : Parcelable + // [END android_kotlin_parcelize_basic] +} + +private object CompanionParcelerSnippet { + // [START android_kotlin_parcelize_companion_parceler] + @Parcelize + data class User(val firstName: String, val lastName: String, val age: Int) : Parcelable { + private companion object : Parceler { + override fun User.write(parcel: Parcel, flags: Int) { + // Custom write implementation + } + + override fun create(parcel: Parcel): User { + // Custom read implementation + // [START_EXCLUDE silent] + return User("", "", 0) + // [END_EXCLUDE] + } + } + } + // [END android_kotlin_parcelize_companion_parceler] +} + +private object ExternalClassParcelerSnippet { + // [START android_kotlin_parcelize_external_class_parceler] + class ExternalClass(val value: Int) + + object ExternalClassParceler : Parceler { + override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) + + override fun ExternalClass.write(parcel: Parcel, flags: Int) { + parcel.writeInt(value) + } + } + // [END android_kotlin_parcelize_external_class_parceler] +} + +private object ClassLocalParcelerSnippet { + class ExternalClass(val value: Int) + + object ExternalClassParceler : Parceler { + override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) + + override fun ExternalClass.write(parcel: Parcel, flags: Int) { + parcel.writeInt(value) + } + } + + // [START android_kotlin_parcelize_type_parceler_class] + // Class-local parceler + @Parcelize + @TypeParceler() + class MyClass(val external: ExternalClass) : Parcelable + // [END android_kotlin_parcelize_type_parceler_class] +} + +private object PropertyLocalParcelerSnippet { + class ExternalClass(val value: Int) + + object ExternalClassParceler : Parceler { + override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) + + override fun ExternalClass.write(parcel: Parcel, flags: Int) { + parcel.writeInt(value) + } + } + + // [START android_kotlin_parcelize_type_parceler_property] + // Property-local parceler + @Parcelize + class MyClass(@TypeParceler() val external: ExternalClass) : Parcelable + // [END android_kotlin_parcelize_type_parceler_property] +} + +private object TypeLocalParcelerSnippet { + class ExternalClass(val value: Int) + + object ExternalClassParceler : Parceler { + override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) + + override fun ExternalClass.write(parcel: Parcel, flags: Int) { + parcel.writeInt(value) + } + } + + // [START android_kotlin_parcelize_type_parceler_type] + // Type-local parceler + @Parcelize + class MyClass(val external: @WriteWith() ExternalClass) : Parcelable + // [END android_kotlin_parcelize_type_parceler_type] +} + +private object ParcelableCreatorSnippet { + @Parcelize + class User(val firstName: String, val lastName: String, val age: Int) : Parcelable + + // [START android_kotlin_parcelize_parcelable_creator] + // import kotlinx.parcelize.parcelableCreator + + fun userFromParcel(parcel: Parcel): User { + return parcelableCreator().createFromParcel(parcel) + } + // [END android_kotlin_parcelize_parcelable_creator] +} + +private object IgnoredOnParcelSnippet { + // [START android_kotlin_parcelize_ignored_on_parcel] + @Parcelize + class MyClass( + val include: String, + // Don't serialize this property + @IgnoredOnParcel val ignore: String = "default" + ) : Parcelable { + // Silence a warning + @IgnoredOnParcel + val computed: String = include + ignore + } + // [END android_kotlin_parcelize_ignored_on_parcel] +} + +private object RawValueSnippet { + class ExternalClass(val value: Int) + + // [START android_kotlin_parcelize_raw_value] + @Parcelize + class MyClass(val external: @RawValue ExternalClass) : Parcelable + // [END android_kotlin_parcelize_raw_value] +} + +private object SealedClassSnippet { + // [START android_kotlin_parcelize_sealed_class] + @Parcelize + sealed class SealedClass : Parcelable { + class A(val a: String) : SealedClass() + class B(val b: Int) : SealedClass() + } + + @Parcelize + class MyClass(val a: SealedClass.A, val b: SealedClass.B, val c: SealedClass) : Parcelable + // [END android_kotlin_parcelize_sealed_class] +} + +private object DataWrapperSnippet { + // [START android_kotlin_parcelize_data_class_wrapper] + // Common code: + data class MyData(val x: String, val y: MoreData) + data class MoreData(val a: String, val b: Int) + + // Platform code: + @OptIn(kotlinx.parcelize.Experimental::class) + @Parcelize + class DataWrapper(val wrapped: @DataClass MyData) : Parcelable + // [END android_kotlin_parcelize_data_class_wrapper] +} + +private object InheritanceBaseSnippet { + // [START android_kotlin_parcelize_inheritance_base] + // base parcelize + @Parcelize + open class Base(open val s: String) : Parcelable + + @Parcelize + class Derived( + val x: Int, + // all arguments have to be `val` or `var` so we need to override + // to not introduce new property name + override val s: String + ) : Base(s) + // [END android_kotlin_parcelize_inheritance_base] +} diff --git a/kotlin/src/main/kotlin/com/example/android/basics/ParcelizeDataClass.kt b/kotlin/src/main/kotlin/com/example/android/basics/ParcelizeDataClass.kt new file mode 100644 index 000000000..6633446d8 --- /dev/null +++ b/kotlin/src/main/kotlin/com/example/android/basics/ParcelizeDataClass.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(kotlinx.parcelize.Experimental::class) + +package com.example.android.basics + +import android.os.Parcelable +import kotlinx.parcelize.DataClass +import kotlinx.parcelize.Parcelize + +private object DataClassSnippet { + // [START android_kotlin_parcelize_data_class] + // @file:OptIn(kotlinx.parcelize.Experimental::class) + + data class C(val a: Int, val b: String) + + @Parcelize + class P(val c: @DataClass C) : Parcelable + // [END android_kotlin_parcelize_data_class] +} From 3f1f66c3b0504ad4e4e9eaf56b683d15a391a622 Mon Sep 17 00:00:00 2001 From: Erik Rodriguez Date: Mon, 7 Sep 2026 23:30:17 -0500 Subject: [PATCH 2/2] Add multiplatform Parcelize snippets Adds the Kotlin 2.0+ multiplatform Parcelize snippets to kmp/shared, per review feedback on the PR: the common code, the Android platform actuals, and the additionalAnnotation Gradle config. iOS actuals are added (empty) because the common expect declarations require an actual in every target. The Gradle config is left untagged and hardcoded on the page. --- kmp/shared/build.gradle.kts | 5 +++ .../com/example/kmp/snippets/Parcelize.kt | 26 +++++++++++++ .../com/example/kmp/snippets/Parcelize.kt | 39 +++++++++++++++++++ .../com/example/kmp/snippets/Parcelize.kt | 23 +++++++++++ 4 files changed, 93 insertions(+) create mode 100644 kmp/shared/src/androidMain/kotlin/com/example/kmp/snippets/Parcelize.kt create mode 100644 kmp/shared/src/commonMain/kotlin/com/example/kmp/snippets/Parcelize.kt create mode 100644 kmp/shared/src/iosMain/kotlin/com/example/kmp/snippets/Parcelize.kt diff --git a/kmp/shared/build.gradle.kts b/kmp/shared/build.gradle.kts index f368db82a..47cb95c93 100644 --- a/kmp/shared/build.gradle.kts +++ b/kmp/shared/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.kotlin.multiplatform.library) + alias(libs.plugins.kotlin.parcelize) alias(libs.plugins.android.lint) } @@ -13,6 +14,10 @@ kotlin { compileSdk = libs.versions.compileSdk.get().toInt() minSdk = libs.versions.minSdk.get().toInt() + compilerOptions { + freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=com.example.kmp.snippets.MyParcelize") + } + withHostTestBuilder { } diff --git a/kmp/shared/src/androidMain/kotlin/com/example/kmp/snippets/Parcelize.kt b/kmp/shared/src/androidMain/kotlin/com/example/kmp/snippets/Parcelize.kt new file mode 100644 index 000000000..761f339ba --- /dev/null +++ b/kmp/shared/src/androidMain/kotlin/com/example/kmp/snippets/Parcelize.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.kmp.snippets + +// [START android_kotlin_parcelize_multiplatform_platform] +// Platform code +// package example + +// No typealias for MyParcelize here +actual typealias MyParcelable = android.os.Parcelable +actual typealias MyIgnoredOnParcel = kotlinx.parcelize.IgnoredOnParcel +// [END android_kotlin_parcelize_multiplatform_platform] diff --git a/kmp/shared/src/commonMain/kotlin/com/example/kmp/snippets/Parcelize.kt b/kmp/shared/src/commonMain/kotlin/com/example/kmp/snippets/Parcelize.kt new file mode 100644 index 000000000..9009cfbfa --- /dev/null +++ b/kmp/shared/src/commonMain/kotlin/com/example/kmp/snippets/Parcelize.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.kmp.snippets + +// [START android_kotlin_parcelize_multiplatform_common] +// Common code +// package example + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +// No `expect` keyword here +annotation class MyParcelize() + +expect interface MyParcelable + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.SOURCE) +expect annotation class MyIgnoredOnParcel() + +@MyParcelize +class MyClass( + val x: String, + @MyIgnoredOnParcel val y: String = "" +) : MyParcelable +// [END android_kotlin_parcelize_multiplatform_common] diff --git a/kmp/shared/src/iosMain/kotlin/com/example/kmp/snippets/Parcelize.kt b/kmp/shared/src/iosMain/kotlin/com/example/kmp/snippets/Parcelize.kt new file mode 100644 index 000000000..f472d8f3d --- /dev/null +++ b/kmp/shared/src/iosMain/kotlin/com/example/kmp/snippets/Parcelize.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.kmp.snippets + +actual interface MyParcelable + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.SOURCE) +actual annotation class MyIgnoredOnParcel()