Loading apex/appsearch/framework/api/current.txt +4 −2 Original line number Diff line number Diff line Loading @@ -289,14 +289,16 @@ package android.app.appsearch { public final class SearchResult { method @NonNull public String getDatabaseName(); method @NonNull public android.app.appsearch.GenericDocument getGenericDocument(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatchInfos(); method @Deprecated @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); method @NonNull public String getPackageName(); method public double getRankingSignal(); } public static final class SearchResult.Builder { ctor public SearchResult.Builder(@NonNull String, @NonNull String); method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @Deprecated @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @NonNull public android.app.appsearch.SearchResult.Builder addMatchInfo(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @NonNull public android.app.appsearch.SearchResult build(); method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument); method @NonNull public android.app.appsearch.SearchResult.Builder setRankingSignal(double); Loading apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +97 −2 Original line number Diff line number Diff line Loading @@ -843,6 +843,20 @@ public class GenericDocument { } } /** * Copies the contents of this {@link GenericDocument} into a new {@link * GenericDocument.Builder}. * * <p>The returned builder is a deep copy whose data is separate from this document. * * @hide */ @NonNull public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() { Bundle clonedBundle = BundleUtil.deepCopy(mBundle); return new GenericDocument.Builder<>(clonedBundle); } @Override public boolean equals(@Nullable Object other) { if (this == other) { Loading Loading @@ -936,8 +950,8 @@ public class GenericDocument { @SuppressLint("StaticFinalBuilder") public static class Builder<BuilderType extends Builder> { private final Bundle mProperties = new Bundle(); private final Bundle mBundle = new Bundle(); private final Bundle mBundle; private final Bundle mProperties; private final BuilderType mBuilderTypeInstance; private boolean mBuilt = false; Loading @@ -964,6 +978,8 @@ public class GenericDocument { Objects.requireNonNull(namespace); Objects.requireNonNull(id); Objects.requireNonNull(schemaType); mBundle = new Bundle(); mBuilderTypeInstance = (BuilderType) this; mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); mBundle.putString(GenericDocument.ID_FIELD, id); Loading @@ -973,9 +989,72 @@ public class GenericDocument { GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); mProperties = new Bundle(); mBundle.putBundle(PROPERTIES_FIELD, mProperties); } /** Creates a new {@link GenericDocument.Builder} from the given Bundle. */ @SuppressWarnings("unchecked") Builder(@NonNull Bundle bundle) { mBundle = Objects.requireNonNull(bundle); mProperties = mBundle.getBundle(PROPERTIES_FIELD); mBuilderTypeInstance = (BuilderType) this; } /** * Sets the app-defined namespace this document resides in, changing the value provided in * the constructor. No special values are reserved or understood by the infrastructure. * * <p>Document IDs are unique within a namespace. * * <p>The number of namespaces per app should be kept small for efficiency reasons. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(namespace); mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); return mBuilderTypeInstance; } /** * Sets the ID of this document, changing the value provided in the constructor. No special * values are reserved or understood by the infrastructure. * * <p>Document IDs are unique within a namespace. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setId(@NonNull String id) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(id); mBundle.putString(GenericDocument.ID_FIELD, id); return mBuilderTypeInstance; } /** * Sets the schema type of this document, changing the value provided in the constructor. * * <p>To successfully index a document, the schema type must match the name of an {@link * AppSearchSchema} object previously provided to {@link AppSearchSession#setSchema}. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setSchemaType(@NonNull String schemaType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(schemaType); mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); return mBuilderTypeInstance; } /** * Sets the score of the {@link GenericDocument}. * Loading Loading @@ -1156,6 +1235,22 @@ public class GenericDocument { return mBuilderTypeInstance; } /** * Clears the value for the property with the given name. * * <p>Note that this method does not support property paths. * * @param name The name of the property to clear. * @hide */ @NonNull public BuilderType clearProperty(@NonNull String name) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(name); mProperties.remove(name); return mBuilderTypeInstance; } private void putInPropertyBundle(@NonNull String name, @NonNull String[] values) throws IllegalArgumentException { validateRepeatedPropertyLength(name, values.length); Loading apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +27 −12 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ import java.util.Objects; * <ul> * <li>The document which matched, using {@link #getGenericDocument} * <li>Information about which properties in the document matched, and "snippet" information * containing textual summaries of the document's matches, using {@link #getMatches} * containing textual summaries of the document's matches, using {@link #getMatchInfos} * </ul> * * <p>"Snippet" refers to a substring of text from the content of document that is returned as a Loading @@ -44,7 +44,7 @@ import java.util.Objects; */ public final class SearchResult { static final String DOCUMENT_FIELD = "document"; static final String MATCHES_FIELD = "matches"; static final String MATCH_INFOS_FIELD = "matchInfos"; static final String PACKAGE_NAME_FIELD = "packageName"; static final String DATABASE_NAME_FIELD = "databaseName"; static final String RANKING_SIGNAL_FIELD = "rankingSignal"; Loading @@ -55,7 +55,7 @@ public final class SearchResult { @Nullable private GenericDocument mDocument; /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ @Nullable private List<MatchInfo> mMatches; @Nullable private List<MatchInfo> mMatchInfos; /** @hide */ public SearchResult(@NonNull Bundle bundle) { Loading @@ -82,8 +82,16 @@ public final class SearchResult { return mDocument; } /** @deprecated This method exists only for dogfooder transition and must be removed. */ @Deprecated @NonNull public List<MatchInfo> getMatches() { return getMatchInfos(); } /** * Contains a list of Snippets that matched the request. * Returns a list of {@link MatchInfo}s providing information about how the document in {@link * #getGenericDocument} matched the query. * * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link * SearchSpec.Builder#setSnippetCount} or {@link Loading @@ -91,17 +99,17 @@ public final class SearchResult { * method returns an empty list. */ @NonNull public List<MatchInfo> getMatches() { if (mMatches == null) { public List<MatchInfo> getMatchInfos() { if (mMatchInfos == null) { List<Bundle> matchBundles = Objects.requireNonNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); mMatches = new ArrayList<>(matchBundles.size()); Objects.requireNonNull(mBundle.getParcelableArrayList(MATCH_INFOS_FIELD)); mMatchInfos = new ArrayList<>(matchBundles.size()); for (int i = 0; i < matchBundles.size(); i++) { MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); mMatches.add(matchInfo); mMatchInfos.add(matchInfo); } } return mMatches; return mMatchInfos; } /** Loading Loading @@ -184,9 +192,16 @@ public final class SearchResult { return this; } /** Adds another match to this SearchResult. */ /** @deprecated this method exists only for dogfooder transition and must be removed */ @Deprecated @NonNull public Builder addMatch(@NonNull MatchInfo matchInfo) { return addMatchInfo(matchInfo); } /** Adds another match to this SearchResult. */ @NonNull public Builder addMatchInfo(@NonNull MatchInfo matchInfo) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState( matchInfo.mDocument == null, Loading @@ -212,7 +227,7 @@ public final class SearchResult { @NonNull public SearchResult build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos); mBundle.putParcelableArrayList(MATCH_INFOS_FIELD, mMatchInfos); mBuilt = true; return new SearchResult(mBundle); } Loading apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +13 −5 Original line number Diff line number Diff line Loading @@ -323,6 +323,7 @@ public final class SearchSpec { mBundle = new Bundle(); mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); mBundle.putInt(TERM_MATCH_TYPE_FIELD, TERM_MATCH_PREFIX); mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, MAX_SNIPPET_PER_PROPERTY_COUNT); } /** Loading Loading @@ -471,8 +472,11 @@ public final class SearchSpec { * Only the first {@code snippetCount} documents based on the ranking strategy will have * snippet information provided. * * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will * return {@code null} for that result. * <p>The list returned from {@link SearchResult#getMatchInfos} will contain at most this * many entries. * * <p>If set to 0 (default), snippeting is disabled and the list returned from {@link * SearchResult#getMatchInfos} will be empty. */ @NonNull public SearchSpec.Builder setSnippetCount( Loading @@ -485,10 +489,14 @@ public final class SearchSpec { /** * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} * snippets for each property of {@link GenericDocument} will contain snippet information. * snippets for each property of each {@link GenericDocument} will contain snippet * information. * * <p>If set to 0, snippeting is disabled and the list returned from {@link * SearchResult#getMatchInfos} will be empty. * * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return * {@code null} for that result. * <p>The default behavior is to snippet all matches a property contains, up to the maximum * value of 10,000. */ @NonNull public SearchSpec.Builder setSnippetCountPerProperty( Loading apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java +24 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.app.appsearch.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.os.Parcel; import android.util.SparseArray; import java.util.ArrayList; Loading Loading @@ -224,4 +226,26 @@ public final class BundleUtil { } return Arrays.hashCode(hashCodes); } /** * Deeply clones a Bundle. * * <p>Values which are Bundles, Lists or Arrays are deeply copied themselves. */ @NonNull public static Bundle deepCopy(@NonNull Bundle bundle) { // Write bundle to bytes Parcel parcel = Parcel.obtain(); try { parcel.writeBundle(bundle); byte[] serializedMessage = parcel.marshall(); // Read bundle from bytes parcel.unmarshall(serializedMessage, 0, serializedMessage.length); parcel.setDataPosition(0); return parcel.readBundle(); } finally { parcel.recycle(); } } } Loading
apex/appsearch/framework/api/current.txt +4 −2 Original line number Diff line number Diff line Loading @@ -289,14 +289,16 @@ package android.app.appsearch { public final class SearchResult { method @NonNull public String getDatabaseName(); method @NonNull public android.app.appsearch.GenericDocument getGenericDocument(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatchInfos(); method @Deprecated @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); method @NonNull public String getPackageName(); method public double getRankingSignal(); } public static final class SearchResult.Builder { ctor public SearchResult.Builder(@NonNull String, @NonNull String); method @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @Deprecated @NonNull public android.app.appsearch.SearchResult.Builder addMatch(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @NonNull public android.app.appsearch.SearchResult.Builder addMatchInfo(@NonNull android.app.appsearch.SearchResult.MatchInfo); method @NonNull public android.app.appsearch.SearchResult build(); method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument); method @NonNull public android.app.appsearch.SearchResult.Builder setRankingSignal(double); Loading
apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +97 −2 Original line number Diff line number Diff line Loading @@ -843,6 +843,20 @@ public class GenericDocument { } } /** * Copies the contents of this {@link GenericDocument} into a new {@link * GenericDocument.Builder}. * * <p>The returned builder is a deep copy whose data is separate from this document. * * @hide */ @NonNull public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() { Bundle clonedBundle = BundleUtil.deepCopy(mBundle); return new GenericDocument.Builder<>(clonedBundle); } @Override public boolean equals(@Nullable Object other) { if (this == other) { Loading Loading @@ -936,8 +950,8 @@ public class GenericDocument { @SuppressLint("StaticFinalBuilder") public static class Builder<BuilderType extends Builder> { private final Bundle mProperties = new Bundle(); private final Bundle mBundle = new Bundle(); private final Bundle mBundle; private final Bundle mProperties; private final BuilderType mBuilderTypeInstance; private boolean mBuilt = false; Loading @@ -964,6 +978,8 @@ public class GenericDocument { Objects.requireNonNull(namespace); Objects.requireNonNull(id); Objects.requireNonNull(schemaType); mBundle = new Bundle(); mBuilderTypeInstance = (BuilderType) this; mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); mBundle.putString(GenericDocument.ID_FIELD, id); Loading @@ -973,9 +989,72 @@ public class GenericDocument { GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); mProperties = new Bundle(); mBundle.putBundle(PROPERTIES_FIELD, mProperties); } /** Creates a new {@link GenericDocument.Builder} from the given Bundle. */ @SuppressWarnings("unchecked") Builder(@NonNull Bundle bundle) { mBundle = Objects.requireNonNull(bundle); mProperties = mBundle.getBundle(PROPERTIES_FIELD); mBuilderTypeInstance = (BuilderType) this; } /** * Sets the app-defined namespace this document resides in, changing the value provided in * the constructor. No special values are reserved or understood by the infrastructure. * * <p>Document IDs are unique within a namespace. * * <p>The number of namespaces per app should be kept small for efficiency reasons. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(namespace); mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); return mBuilderTypeInstance; } /** * Sets the ID of this document, changing the value provided in the constructor. No special * values are reserved or understood by the infrastructure. * * <p>Document IDs are unique within a namespace. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setId(@NonNull String id) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(id); mBundle.putString(GenericDocument.ID_FIELD, id); return mBuilderTypeInstance; } /** * Sets the schema type of this document, changing the value provided in the constructor. * * <p>To successfully index a document, the schema type must match the name of an {@link * AppSearchSchema} object previously provided to {@link AppSearchSession#setSchema}. * * @throws IllegalStateException if the builder has already been used. * @hide */ @NonNull public BuilderType setSchemaType(@NonNull String schemaType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(schemaType); mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); return mBuilderTypeInstance; } /** * Sets the score of the {@link GenericDocument}. * Loading Loading @@ -1156,6 +1235,22 @@ public class GenericDocument { return mBuilderTypeInstance; } /** * Clears the value for the property with the given name. * * <p>Note that this method does not support property paths. * * @param name The name of the property to clear. * @hide */ @NonNull public BuilderType clearProperty(@NonNull String name) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Objects.requireNonNull(name); mProperties.remove(name); return mBuilderTypeInstance; } private void putInPropertyBundle(@NonNull String name, @NonNull String[] values) throws IllegalArgumentException { validateRepeatedPropertyLength(name, values.length); Loading
apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +27 −12 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ import java.util.Objects; * <ul> * <li>The document which matched, using {@link #getGenericDocument} * <li>Information about which properties in the document matched, and "snippet" information * containing textual summaries of the document's matches, using {@link #getMatches} * containing textual summaries of the document's matches, using {@link #getMatchInfos} * </ul> * * <p>"Snippet" refers to a substring of text from the content of document that is returned as a Loading @@ -44,7 +44,7 @@ import java.util.Objects; */ public final class SearchResult { static final String DOCUMENT_FIELD = "document"; static final String MATCHES_FIELD = "matches"; static final String MATCH_INFOS_FIELD = "matchInfos"; static final String PACKAGE_NAME_FIELD = "packageName"; static final String DATABASE_NAME_FIELD = "databaseName"; static final String RANKING_SIGNAL_FIELD = "rankingSignal"; Loading @@ -55,7 +55,7 @@ public final class SearchResult { @Nullable private GenericDocument mDocument; /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ @Nullable private List<MatchInfo> mMatches; @Nullable private List<MatchInfo> mMatchInfos; /** @hide */ public SearchResult(@NonNull Bundle bundle) { Loading @@ -82,8 +82,16 @@ public final class SearchResult { return mDocument; } /** @deprecated This method exists only for dogfooder transition and must be removed. */ @Deprecated @NonNull public List<MatchInfo> getMatches() { return getMatchInfos(); } /** * Contains a list of Snippets that matched the request. * Returns a list of {@link MatchInfo}s providing information about how the document in {@link * #getGenericDocument} matched the query. * * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link * SearchSpec.Builder#setSnippetCount} or {@link Loading @@ -91,17 +99,17 @@ public final class SearchResult { * method returns an empty list. */ @NonNull public List<MatchInfo> getMatches() { if (mMatches == null) { public List<MatchInfo> getMatchInfos() { if (mMatchInfos == null) { List<Bundle> matchBundles = Objects.requireNonNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); mMatches = new ArrayList<>(matchBundles.size()); Objects.requireNonNull(mBundle.getParcelableArrayList(MATCH_INFOS_FIELD)); mMatchInfos = new ArrayList<>(matchBundles.size()); for (int i = 0; i < matchBundles.size(); i++) { MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); mMatches.add(matchInfo); mMatchInfos.add(matchInfo); } } return mMatches; return mMatchInfos; } /** Loading Loading @@ -184,9 +192,16 @@ public final class SearchResult { return this; } /** Adds another match to this SearchResult. */ /** @deprecated this method exists only for dogfooder transition and must be removed */ @Deprecated @NonNull public Builder addMatch(@NonNull MatchInfo matchInfo) { return addMatchInfo(matchInfo); } /** Adds another match to this SearchResult. */ @NonNull public Builder addMatchInfo(@NonNull MatchInfo matchInfo) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState( matchInfo.mDocument == null, Loading @@ -212,7 +227,7 @@ public final class SearchResult { @NonNull public SearchResult build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBundle.putParcelableArrayList(MATCHES_FIELD, mMatchInfos); mBundle.putParcelableArrayList(MATCH_INFOS_FIELD, mMatchInfos); mBuilt = true; return new SearchResult(mBundle); } Loading
apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +13 −5 Original line number Diff line number Diff line Loading @@ -323,6 +323,7 @@ public final class SearchSpec { mBundle = new Bundle(); mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); mBundle.putInt(TERM_MATCH_TYPE_FIELD, TERM_MATCH_PREFIX); mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, MAX_SNIPPET_PER_PROPERTY_COUNT); } /** Loading Loading @@ -471,8 +472,11 @@ public final class SearchSpec { * Only the first {@code snippetCount} documents based on the ranking strategy will have * snippet information provided. * * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will * return {@code null} for that result. * <p>The list returned from {@link SearchResult#getMatchInfos} will contain at most this * many entries. * * <p>If set to 0 (default), snippeting is disabled and the list returned from {@link * SearchResult#getMatchInfos} will be empty. */ @NonNull public SearchSpec.Builder setSnippetCount( Loading @@ -485,10 +489,14 @@ public final class SearchSpec { /** * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} * snippets for each property of {@link GenericDocument} will contain snippet information. * snippets for each property of each {@link GenericDocument} will contain snippet * information. * * <p>If set to 0, snippeting is disabled and the list returned from {@link * SearchResult#getMatchInfos} will be empty. * * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return * {@code null} for that result. * <p>The default behavior is to snippet all matches a property contains, up to the maximum * value of 10,000. */ @NonNull public SearchSpec.Builder setSnippetCountPerProperty( Loading
apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java +24 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.app.appsearch.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.os.Parcel; import android.util.SparseArray; import java.util.ArrayList; Loading Loading @@ -224,4 +226,26 @@ public final class BundleUtil { } return Arrays.hashCode(hashCodes); } /** * Deeply clones a Bundle. * * <p>Values which are Bundles, Lists or Arrays are deeply copied themselves. */ @NonNull public static Bundle deepCopy(@NonNull Bundle bundle) { // Write bundle to bytes Parcel parcel = Parcel.obtain(); try { parcel.writeBundle(bundle); byte[] serializedMessage = parcel.marshall(); // Read bundle from bytes parcel.unmarshall(serializedMessage, 0, serializedMessage.length); parcel.setDataPosition(0); return parcel.readBundle(); } finally { parcel.recycle(); } } }