Home | History | Annotate | Download | only in internal
      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