Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 998008ae authored by Jacky Wang's avatar Jacky Wang
Browse files

[Catalyst] Add extensions for Parcel

Bug: 418243848
Flag: EXEMPT library
Test: Unit test
Change-Id: I672841736519e3109ab46de8dc86b8d07e011108
parent 94a8a518
Loading
Loading
Loading
Loading
+67 −20
Original line number Diff line number Diff line
@@ -18,16 +18,23 @@ package com.android.settingslib.metadata

import android.content.Intent
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable

/** Returns if the two bundles are equal (`null` values are ignored). */
@Suppress("DEPRECATION")
fun Bundle?.contentEquals(other: Bundle?): Boolean {
infix fun Bundle?.contentEquals(other: Bundle?): Boolean {
    if (this == null) return other == null
    if (other == null) return false
    if (keySet() != other.keySet()) return false
    fun Any?.valueEquals(other: Any?) =
    // entry could have null value, so compare all keys
    val keys = keySet() + other.keySet()
    return keys.all { key -> get(key).valueEquals(other.get(key)) }
}

private fun Any?.valueEquals(other: Any?) =
    when (this) {
            is Bundle -> other is Bundle && this.contentEquals(other)
            is Intent -> other is Intent && this.filterEquals(other)
        is Bundle -> other is Bundle && contentEquals(other)
        is Intent -> other is Intent && filterEquals(other) && extras contentEquals other.extras
        is BooleanArray -> other is BooleanArray && this contentEquals other
        is ByteArray -> other is ByteArray && this contentEquals other
        is CharArray -> other is CharArray && this contentEquals other
@@ -39,8 +46,48 @@ fun Bundle?.contentEquals(other: Bundle?): Boolean {
        is Array<*> -> other is Array<*> && this contentDeepEquals other
        else -> this == other
    }
    for (key in keySet()) {
        if (!get(key).valueEquals(other.get(key))) return false

/** Marshall a [Parcelable] to byte array. */
fun Parcelable.marshallParcel(): ByteArray = useParcel { parcel ->
    writeToParcel(parcel, 0)
    return@useParcel parcel.marshall()
}

/**
 * Unmarshall a byte array to [Bundle].
 *
 * Proper [ClassLoader] should be provided if needed (e.g. [Parcelable]) when read entry.
 */
fun ByteArray.unmarshallBundle(): Bundle = unmarshallParcel(Bundle.CREATOR)

/** Unmarshall a byte array to [Parcelable]. */
inline fun <reified T> ByteArray.unmarshallParcel(creator: Parcelable.Creator<T>): T =
    useParcel { parcel ->
        parcel.unmarshall(this, 0, size)
        parcel.setDataPosition(0)
        return@useParcel creator.createFromParcel(parcel)
    }

/** Unmarshall a byte array to [Parcelable] with given class loader. */
inline fun <reified T> ByteArray.unmarshallParcel(
    creator: Parcelable.ClassLoaderCreator<T>,
    classLoader: ClassLoader,
): T = useParcel { parcel ->
    parcel.unmarshall(this, 0, size)
    parcel.setDataPosition(0)
    return@useParcel creator.createFromParcel(parcel, classLoader)
}

/**
 * Obtains a [Parcel] and performs an action.
 *
 * The parcel is ensured to be recycled when exception is thrown.
 */
fun <R> useParcel(block: (Parcel) -> R): R {
    val parcel = Parcel.obtain()
    try {
        return block(parcel)
    } finally {
        parcel.recycle()
    }
    return true
}
+23 −0
Original line number Diff line number Diff line
package {
    default_applicable_licenses: ["frameworks_base_license"],
}

android_app {
    name: "SettingsLibMetadataShell",
    platform_apis: true,
}

android_robolectric_test {
    name: "SettingsLibMetadataTest",
    srcs: ["src/**/*.kt"],
    static_libs: [
        "SettingsLibMetadata",
        "androidx.test.ext.junit",
        "kotlin-parcelize-runtime",
        "truth",
    ],
    kotlin_plugins: ["kotlin-parcelize-compiler-plugin"],
    instrumentation_for: "SettingsLibMetadataShell",
    coverage_libs: ["SettingsLibMetadata"],
    upstream: true,
}
+2 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.android.settingslib.metadata.test" />
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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
 *
 *      http://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.android.settingslib.metadata

import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.parcelableCreator
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class BundlesTest {

    @Test
    fun bundle_marshall_unmarshall() {
        val userHandle = Process.myUserHandle()
        val bundle =
            Bundle().apply {
                putString("nullString", null)
                putString("string", "string")
                putByteArray("bytes", byteArrayOf(1))
                putInt("int", 1)
                putLong("long", 2)
                putBoolean("boolean", true)
                putDouble("double", 3.0)
                putFloat("float", 4f)
                putParcelable(Intent.EXTRA_USER, userHandle)
                putParcelable("foo", Foo("foo"))
                putParcelable("bar", Bar("bar"))
            }
        val result = bundle.marshallParcel().unmarshallBundle()
        result.classLoader = Foo::class.java.classLoader
        @Suppress("DEPRECATION") val foo = result.getParcelable<Foo>("foo")!!
        assertThat(foo.name).isEqualTo("foo")
        result.classLoader = Bar::class.java.classLoader
        assertThat(bundle contentEquals result).isTrue()
    }

    @Test
    fun marshallParcel_unmarshallParcel() {
        val parcelable = Foo("foo")
        val result = parcelable.marshallParcel().unmarshallParcel(parcelableCreator<Foo>())
        assertThat(result).isEqualTo(parcelable)
    }

    @Parcelize data class Foo(val name: String) : Parcelable

    @Parcelize data class Bar(val name: String) : Parcelable
}