summaryrefslogtreecommitdiff
path: root/main/java/com/google/android/setupcompat/internal/PersistableBundles.java
blob: f02265a168dc3a51fa9445ec6c0adb43b5e79396 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * Copyright (C) 2018 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.google.android.setupcompat.internal;

import android.annotation.TargetApi;
import android.os.BaseBundle;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/** Contains utility methods related to {@link PersistableBundle}. */
@TargetApi(VERSION_CODES.LOLLIPOP_MR1)
public final class PersistableBundles {

  /**
   * Merges two or more {@link PersistableBundle}. Ensures no conflict of keys occurred during
   * merge.
   *
   * @return Returns a new {@link PersistableBundle} that contains all the data from {@code
   *     firstBundle}, {@code nextBundle} and {@code others}.
   */
  public static PersistableBundle mergeBundles(
      PersistableBundle firstBundle, PersistableBundle nextBundle, PersistableBundle... others) {
    List<PersistableBundle> allBundles = new ArrayList<>();
    allBundles.addAll(Arrays.asList(firstBundle, nextBundle));
    Collections.addAll(allBundles, others);

    PersistableBundle result = new PersistableBundle();
    for (PersistableBundle bundle : allBundles) {
      for (String key : bundle.keySet()) {
        Preconditions.checkArgument(
            !result.containsKey(key),
            String.format("Found duplicate key [%s] while attempting to merge bundles.", key));
      }
      result.putAll(bundle);
    }

    return result;
  }

  /** Returns a {@link Bundle} that contains all the values from {@code persistableBundle}. */
  public static Bundle toBundle(PersistableBundle persistableBundle) {
    Bundle bundle = new Bundle();
    bundle.putAll(persistableBundle);
    return bundle;
  }

  /**
   * Returns a {@link PersistableBundle} that contains values from {@code bundle} that are supported
   * by the logging API. Un-supported value types are dropped.
   */
  public static PersistableBundle fromBundle(Bundle bundle) {
    PersistableBundle to = new PersistableBundle();
    ArrayMap<String, Object> map = toMap(bundle);
    for (String key : map.keySet()) {
      Object value = map.get(key);
      if (value instanceof Long) {
        to.putLong(key, (Long) value);
      } else if (value instanceof Integer) {
        to.putInt(key, (Integer) value);
      } else if (value instanceof Double) {
        to.putDouble(key, (Double) value);
      } else if (value instanceof Boolean) {
        to.putBoolean(key, (Boolean) value);
      } else if (value instanceof String) {
        to.putString(key, (String) value);
      } else {
        throw new AssertionError(String.format("Missing put* for valid data type? = %s", value));
      }
    }
    return to;
  }

  /** Returns {@code true} if {@code left} contains same set of values as {@code right}. */
  public static boolean equals(PersistableBundle left, PersistableBundle right) {
    return (left == right) || toMap(left).equals(toMap(right));
  }

  /** Asserts that {@code persistableBundle} contains only supported data types. */
  public static PersistableBundle assertIsValid(PersistableBundle persistableBundle) {
    Preconditions.checkNotNull(persistableBundle, "PersistableBundle cannot be null!");
    for (String key : persistableBundle.keySet()) {
      Object value = persistableBundle.get(key);
      Preconditions.checkArgument(
          isSupportedDataType(value),
          String.format("Unknown/unsupported data type [%s] for key %s", value, key));
    }
    return persistableBundle;
  }

  /**
   * Returns a new {@link ArrayMap} that contains values from {@code bundle} that are supported by
   * the logging API.
   */
  private static ArrayMap<String, Object> toMap(BaseBundle baseBundle) {
    if (baseBundle == null || baseBundle.isEmpty()) {
      return new ArrayMap<>(0);
    }

    ArrayMap<String, Object> map = new ArrayMap<>(baseBundle.size());
    for (String key : baseBundle.keySet()) {
      Object value = baseBundle.get(key);
      if (!isSupportedDataType(value)) {
        Log.w(TAG, String.format("Unknown/unsupported data type [%s] for key %s", value, key));
        continue;
      }
      map.put(key, baseBundle.get(key));
    }
    return map;
  }

  private static boolean isSupportedDataType(Object value) {
    return value instanceof Integer
        || value instanceof Long
        || value instanceof Double
        || value instanceof Float
        || value instanceof String
        || value instanceof Boolean;
  }

  private PersistableBundles() {
    throw new AssertionError("Should not be instantiated");
  }

  private static final String TAG = "SetupCompat.PersistBls";
}