blob: 54341a2df793d44a69865d05fcd7b8b2f06df2cb [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* 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.libraries.mobiledatadownload.internal.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import com.google.common.base.Optional;
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
import java.io.IOException;
import javax.annotation.Nullable;
/**
* Simple util to read/write protos from/to {@link SharedPreferences}.
*
* <p>Protos are serialized, and the binary value is base-64 encoded without padding or wrapping.
*/
@CheckReturnValue
public class SharedPreferencesUtil {
/**
* Reads the shared pref value corresponding to the specified key as a lite proto of type 'T'. The
* read value is populated to 'protoValue' which should already be constructed by the caller.
*
* @return the proto or null if no such element was found or could not be parsed.
*/
@Nullable
public static <K extends MessageLite, V extends MessageLite> V readProto(
SharedPreferences prefs, K liteKey, Parser<V> parser) {
return readProto(prefs, serializeProto(liteKey), parser);
}
/**
* Reads the shared pref value corresponding to the specified key as a lite proto of type 'T'. The
* read value is populated to 'protoValue' which should already be constructed by the caller.
*
* @return the proto or null if no such element was found or could not be parse.
*/
@Nullable
public static <T extends MessageLite> T readProto(
SharedPreferences prefs, String key, Parser<T> parser) {
String encodedLiteString = prefs.getString(key, null);
if (encodedLiteString == null) {
return null;
}
try {
return parseLiteFromEncodedString(encodedLiteString, parser);
} catch (InvalidProtocolBufferException e) {
return null;
}
}
/**
* Write and commit the serialized form of the proto into pref, corresponding to the give proto
* key.
*/
public static <T extends MessageLite> boolean writeProto(
SharedPreferences prefs, final String key, final T protoValue) {
SharedPreferences.Editor editor = prefs.edit();
writeProto(editor, key, protoValue);
return editor.commit();
}
/**
* Write and commit the serialized form of the proto into pref, corresponding to the give proto
* key.
*/
public static <K extends MessageLite, T extends MessageLite> boolean writeProto(
SharedPreferences prefs, final K protoKey, final T protoValue) {
SharedPreferences.Editor editor = prefs.edit();
writeProto(editor, protoKey, protoValue);
return editor.commit();
}
/**
* Write the serialized form of the proto into shared pref, corresponding to the given proto key.
*/
public static <K extends MessageLite, T extends MessageLite> void writeProto(
SharedPreferences.Editor editor, final K protoKey, final T protoValue) {
writeProto(editor, serializeProto(protoKey), protoValue);
}
/**
* Write the serialized form of the proto into shared pref, corresponding to the given string key.
*/
public static <T extends MessageLite> void writeProto(
SharedPreferences.Editor editor, final String key, final T protoValue) {
editor.putString(key, serializeProto(protoValue));
}
/** Removes whatever value corresponds the protoKey from shared prefs. */
public static <K extends MessageLite> boolean removeProto(
SharedPreferences prefs, String protoKey) {
return prefs.edit().remove(protoKey).commit();
}
/** Removes whatever value corresponds the protoKey from shared prefs. */
public static <K extends MessageLite> void removeProto(
SharedPreferences.Editor editor, final K protoKey) {
editor.remove(serializeProto(protoKey));
}
/** Removes whatever value corresponds the protoKey from shared prefs. */
public static void removeProto(SharedPreferences.Editor editor, String protoKey) {
editor.remove(protoKey);
}
/** Converts a MessageLite to a string that can be used as a key in shared prefs. */
public static String serializeProto(MessageLite lite) {
byte[] byteValue = lite.toByteArray();
return Base64.encodeToString(byteValue, Base64.NO_PADDING | Base64.NO_WRAP);
}
/**
* Parses a MessageLite from the base64 encoded string.
*
* @return the proto.
*/
public static <T extends MessageLite> T parseLiteFromEncodedString(
String base64Encoded, Parser<T> parser) throws InvalidProtocolBufferException {
byte[] byteValue;
try {
byteValue = Base64.decode(base64Encoded, Base64.NO_PADDING | Base64.NO_WRAP);
} catch (IllegalArgumentException e) {
throw new InvalidProtocolBufferException(
"Unable to decode to byte array", new IOException(e));
}
// Cannot use generated registry here, because it may cause NPE to clients.
// For more detail, see b/140135059.
return parser.parseFrom(byteValue, ExtensionRegistryLite.getEmptyRegistry());
}
/** Returns the SharedPreferences name for {@code instanceId}. */
// TODO(b/204094591): determine whether instanceId is ever actually null.
public static String getSharedPreferencesName(String baseName, Optional<String> instanceId) {
return instanceId != null && instanceId.isPresent() ? baseName + instanceId.get() : baseName;
}
/** Return the SharedPreferences for InstanceId */
// TODO(b/204094591): determine whether instanceId is ever actually null.
public static SharedPreferences getSharedPreferences(
Context context, String baseName, Optional<String> instanceId) {
return context.getSharedPreferences(
getSharedPreferencesName(baseName, instanceId), Context.MODE_PRIVATE);
}
}