56

Is there an easy way to convert a Map to a Bundle in android without explicit iteration?

Why?

Firebase returns a Map for Notification getData(). I need to pass the data to an intent. Formerly GCM gave me a bundle, so I didn't need to worry about this.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Nouvel Travay
  • 6,292
  • 13
  • 40
  • 65
  • 3
    "Is there an easy way to convert a Map to a Bundle in android without explicit iteration?" -- note that a `Map` does not necessarily equate to a `Bundle`. A `Map` can have keys that are not strings. A `Map` can have values that are not one of the supported data types. – CommonsWare May 26 '16 at 19:16
  • 1
    Yes, but in my case they are all primitives and String – Nouvel Travay May 26 '16 at 19:18
  • You could serialize it. – cyroxis May 26 '16 at 19:53
  • 1
    The RemoteMessage object is parcelable, you could just send that directly in the intent – morepork Oct 03 '17 at 21:17

10 Answers10

59

I guess a good old fashioned for loop is the easiest way:

    Bundle bundle = new Bundle();
    for (Map.Entry<String, String> entry : getData().entrySet()) {
        bundle.putString(entry.getKey(), entry.getValue());
    }
Marcin Koziński
  • 10,835
  • 3
  • 47
  • 61
  • 6
    this will probably throw an exception because first parameter "google_sent_time" has a Long type and you are using `putString`. you better use the intent to handle type variation for you and use `intent.putExtra(entry.getKey(), entry.getValue())` so different data types dont make problem for you. – Amir Ziarati Aug 23 '17 at 11:20
  • 2
    @AmirZiarati I don't think that is true anymore, `RemoteMessage#getData()` now returns a `Map` excluding a bunch of the internal values. – Mikael Ohlson Sep 11 '18 at 09:26
27

Neat Kotlin solution using spread operator will look like that:

fun Map<String, Any?>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())
bgplaya
  • 1,105
  • 15
  • 27
  • 1
    Worth noting is that the spread operator is not the best for performance and the above solution will generate a performance warning in some static analysis tools like for example Detekt: https://detekt.github.io/detekt/performance.html#spreadoperator – patrick.elmquist Dec 22 '21 at 07:57
  • worth mentioning that this won't create nested bundles for nested maps, in case you're using something like FIrebase Analytics that requires that – kassim Nov 17 '22 at 11:04
16

Came across this same issue with firebase messaging and created a kotlin extension function for it. The gist is here, code below. Although I am using this method there are some caveats:

  • it doesn't cover all of the types that can be put into a bundle
  • it is still under development and hasn't been fully tested

With this in mind, please use it as a guide not a definitive solution. I will keep the gist up to date as it evolves.

import android.os.Bundle 
import android.os.IBinder
import android.os.Parcelable
import java.io.Serializable

fun <V> Map<String, V>.toBundle(bundle: Bundle = Bundle()): Bundle = bundle.apply {
  forEach {
    val k = it.key
    val v = it.value
    when (v) {
      is IBinder -> putBinder(k, v)
      is Bundle -> putBundle(k, v)
      is Byte -> putByte(k, v)
      is ByteArray -> putByteArray(k, v)
      is Char -> putChar(k, v)
      is CharArray -> putCharArray(k, v)
      is CharSequence -> putCharSequence(k, v)
      is Float -> putFloat(k, v)
      is FloatArray -> putFloatArray(k, v)
      is Parcelable -> putParcelable(k, v)
      is Serializable -> putSerializable(k, v)
      is Short -> putShort(k, v)
      is ShortArray -> putShortArray(k, v)

//      is Size -> putSize(k, v) //api 21
//      is SizeF -> putSizeF(k, v) //api 21

      else -> throw IllegalArgumentException("$v is of a type that is not currently supported")
//      is Array<*> -> TODO()
//      is List<*> -> TODO()
    }
  }
}
Bulwinkel
  • 2,111
  • 25
  • 23
12

Nowadays you can use fun bundleOf(vararg pairs: Pair<String, Any?>)

bakua
  • 13,704
  • 7
  • 43
  • 62
1

You can use writeToParcel(Parcel out, int flags)to generate a Parcel that could be similarly useful, since it's a parent class of Bundle, and it's handily built into the Firebase framework as part of the RemoteMessage class. Documentation is here.

Pierre-Luc Paour
  • 1,725
  • 17
  • 21
SQLiteNoob
  • 2,958
  • 3
  • 29
  • 44
1

here is how I did it in Kotlin

val bundle = Bundle()

for (entry in data.entries)
    bundle.putString(entry.key, entry.value)
Dan Alboteanu
  • 9,404
  • 1
  • 52
  • 40
0

In Kotlin you can build a function extension to convert Map to Bundle without explicit iteration like so:

override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val payload = remoteMessage.data.toBundle()
        Log.d(TAG, "Notification payload: $payload")
        // ...
}

where toBundle() is defined like so:

inline fun <reified T> Map<String, T>.toBundle(): Bundle = bundleOf(*toList<String, T?>().toTypedArray())

or again:

inline fun <reified T> Map<String, T>.toBundle(): Bundle = bundleOf(*map { it.key to it.value }.toTypedArray())
vitiello.antonio
  • 323
  • 3
  • 12
0

In a kotlin way:

private fun Map<String, Any>.toBundle(): Bundle {
    val pairs = this.map { entry ->
        Pair(entry.key, entry.value)
    }.toTypedArray()
    return bundleOf(*pairs)
}
LaurentY
  • 7,495
  • 3
  • 37
  • 55
-1

For future reference

Convert Map to String

  fun Map<String, String>.convertExtraMapToString(): String? {
    return keys.stream()
        .map { key: String -> "$key=" + this[key] }.collect(Collectors.joining(", ", "{", "}"))
}

Convert String to Map then to Bundle

fun String.convertExtraMapToBundle(): Bundle {
    return Arrays.stream(split(",".toRegex()).toTypedArray())
        .map { entry: String -> entry.split("=".toRegex()).toTypedArray() }
        .collect(Collectors.toMap(
            { entry: Array<String> -> entry[0] }) { entry: Array<String> -> entry[1] }).let {
            bundleOf(*it.toList().toTypedArray())
        }
}
Anoop M Maddasseri
  • 10,213
  • 3
  • 52
  • 73
-10
 private ArrayList<Bundle> convertMapToBundleList(ArrayList<HashMap<String, String>> mapList)
Nikolay Mihaylov
  • 3,868
  • 8
  • 27
  • 32