1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.setupcompat.internal; 18 19 import android.annotation.TargetApi; 20 import android.os.BaseBundle; 21 import android.os.Build.VERSION_CODES; 22 import android.os.Bundle; 23 import android.os.PersistableBundle; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collections; 29 import java.util.List; 30 31 /** Contains utility methods related to {@link PersistableBundle}. */ 32 @TargetApi(VERSION_CODES.Q) 33 public final class PersistableBundles { 34 35 /** 36 * Merges two or more {@link PersistableBundle}. Ensures no conflict of keys occurred during 37 * merge. 38 * 39 * @return Returns a new {@link PersistableBundle} that contains all the data from {@code 40 * firstBundle}, {@code nextBundle} and {@code others}. 41 */ 42 public static PersistableBundle mergeBundles( 43 PersistableBundle firstBundle, PersistableBundle nextBundle, PersistableBundle... others) { 44 List<PersistableBundle> allBundles = new ArrayList<>(); 45 allBundles.addAll(Arrays.asList(firstBundle, nextBundle)); 46 Collections.addAll(allBundles, others); 47 48 PersistableBundle result = new PersistableBundle(); 49 for (PersistableBundle bundle : allBundles) { 50 for (String key : bundle.keySet()) { 51 Preconditions.checkArgument( 52 !result.containsKey(key), 53 String.format("Found duplicate key [%s] while attempting to merge bundles.", key)); 54 } 55 result.putAll(bundle); 56 } 57 58 return result; 59 } 60 61 /** Returns a {@link Bundle} that contains all the values from {@code persistableBundle}. */ 62 public static Bundle toBundle(PersistableBundle persistableBundle) { 63 Bundle bundle = new Bundle(); 64 bundle.putAll(persistableBundle); 65 return bundle; 66 } 67 68 /** 69 * Returns a {@link PersistableBundle} that contains values from {@code bundle} that are supported 70 * by the logging API. Un-supported value types are dropped. 71 */ 72 public static PersistableBundle fromBundle(Bundle bundle) { 73 PersistableBundle to = new PersistableBundle(); 74 ArrayMap<String, Object> map = toMap(bundle); 75 for (String key : map.keySet()) { 76 Object value = map.get(key); 77 if (value instanceof Long) { 78 to.putLong(key, (Long) value); 79 } else if (value instanceof Integer) { 80 to.putInt(key, (Integer) value); 81 } else if (value instanceof Double) { 82 to.putDouble(key, (Double) value); 83 } else if (value instanceof Boolean) { 84 to.putBoolean(key, (Boolean) value); 85 } else if (value instanceof String) { 86 to.putString(key, (String) value); 87 } else { 88 throw new AssertionError(String.format("Missing put* for valid data type? = %s", value)); 89 } 90 } 91 return to; 92 } 93 94 /** Returns {@code true} if {@code left} contains same set of values as {@code right}. */ 95 public static boolean equals(PersistableBundle left, PersistableBundle right) { 96 return (left == right) || toMap(left).equals(toMap(right)); 97 } 98 99 /** Asserts that {@code persistableBundle} contains only supported data types. */ 100 public static PersistableBundle assertIsValid(PersistableBundle persistableBundle) { 101 Preconditions.checkNotNull(persistableBundle, "PersistableBundle cannot be null!"); 102 for (String key : persistableBundle.keySet()) { 103 Object value = persistableBundle.get(key); 104 Preconditions.checkArgument( 105 isSupportedDataType(value), 106 String.format("Unknown/unsupported data type [%s] for key %s", value, key)); 107 } 108 return persistableBundle; 109 } 110 111 /** 112 * Returns a new {@link ArrayMap} that contains values from {@code bundle} that are supported by 113 * the logging API. 114 */ 115 private static ArrayMap<String, Object> toMap(BaseBundle baseBundle) { 116 if (baseBundle == null || baseBundle.isEmpty()) { 117 return new ArrayMap<>(0); 118 } 119 120 ArrayMap<String, Object> map = new ArrayMap<>(baseBundle.size()); 121 for (String key : baseBundle.keySet()) { 122 Object value = baseBundle.get(key); 123 if (!isSupportedDataType(value)) { 124 Log.w(TAG, String.format("Unknown/unsupported data type [%s] for key %s", value, key)); 125 continue; 126 } 127 map.put(key, baseBundle.get(key)); 128 } 129 return map; 130 } 131 132 private static boolean isSupportedDataType(Object value) { 133 return value instanceof Integer 134 || value instanceof Long 135 || value instanceof Double 136 || value instanceof Float 137 || value instanceof String 138 || value instanceof Boolean; 139 } 140 141 private PersistableBundles() { 142 throw new AssertionError("Should not be instantiated"); 143 } 144 145 private static final String TAG = "SetupCompat.PersistBls"; 146 } 147