// Copyright 2021-2023 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 // // https://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. #include "internal/platform/implementation/windows/preferences_manager.h" #include #include #include #include #include #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "absl/types/span.h" #include "nlohmann/json.hpp" #include "nlohmann/json_fwd.hpp" #include "internal/base/file_path.h" #include "internal/platform/implementation/windows/preferences_repository.h" #include "internal/platform/logging.h" #include "google/protobuf/json/json.h" #include "google/protobuf/message.h" namespace nearby::windows { namespace { using json = ::nlohmann::json; } // namespace PreferencesManager::PreferencesManager(FilePath preferences_dir) { preferences_repository_ = std::make_unique(preferences_dir); value_ = preferences_repository_->LoadPreferences(); } bool PreferencesManager::Set(absl::string_view key, const json& value) { absl::MutexLock lock(mutex_); return SetValue(key, value); } bool PreferencesManager::SetBoolean(absl::string_view key, bool value) { absl::MutexLock lock(mutex_); return SetValue(key, value); } bool PreferencesManager::SetInteger(absl::string_view key, int value) { absl::MutexLock lock(mutex_); return SetValue(key, value); } bool PreferencesManager::SetInt64(absl::string_view key, int64_t value) { absl::MutexLock lock(mutex_); return SetValue(key, value); } bool PreferencesManager::SetString(absl::string_view key, absl::string_view value) { absl::MutexLock lock(mutex_); return SetValue(key, absl::StrCat(value)); } bool PreferencesManager::SetBooleanArray(absl::string_view key, absl::Span value) { absl::MutexLock lock(mutex_); return SetArrayValue(key, value); } bool PreferencesManager::SetIntegerArray(absl::string_view key, absl::Span value) { absl::MutexLock lock(mutex_); return SetArrayValue(key, value); } bool PreferencesManager::SetInt64Array(absl::string_view key, absl::Span value) { absl::MutexLock lock(mutex_); return SetArrayValue(key, value); } bool PreferencesManager::SetStringArray(absl::string_view key, absl::Span value) { absl::MutexLock lock(mutex_); return SetArrayValue(key, value); } bool PreferencesManager::SetTime(absl::string_view key, absl::Time value) { // Save time as nanos absl::MutexLock lock(mutex_); int64_t tt = absl::ToUnixNanos(value); if (value_[absl::StrCat(key)] == tt) { return false; } value_[absl::StrCat(key)] = tt; return Commit(); } bool PreferencesManager::SetProtoMessage(absl::string_view key, const google::protobuf::Message& value) { std::string json_string; if (!proto2::json::MessageToJsonString(value, &json_string).ok()) { return false; } { absl::MutexLock lock(mutex_); return SetValue(key, json::parse(json_string)); } } // Get JSON value. json PreferencesManager::Get(absl::string_view key, const json& default_value) const { absl::MutexLock lock(mutex_); return GetValue(key, default_value); } bool PreferencesManager::GetBoolean(absl::string_view key, bool default_value) const { absl::MutexLock lock(mutex_); return GetValue(key, default_value); } int PreferencesManager::GetInteger(absl::string_view key, int default_value) const { absl::MutexLock lock(mutex_); return GetValue(key, default_value); } int64_t PreferencesManager::GetInt64(absl::string_view key, int64_t default_value) const { absl::MutexLock lock(mutex_); return GetValue(key, default_value); } std::string PreferencesManager::GetString( absl::string_view key, const std::string& default_value) const { absl::MutexLock lock(mutex_); return GetValue(key, default_value); } std::vector PreferencesManager::GetBooleanArray( absl::string_view key, absl::Span default_value) const { absl::MutexLock lock(mutex_); return GetArrayValue(key, default_value); } std::vector PreferencesManager::GetIntegerArray( absl::string_view key, absl::Span default_value) const { absl::MutexLock lock(mutex_); return GetArrayValue(key, default_value); } std::vector PreferencesManager::GetInt64Array( absl::string_view key, absl::Span default_value) const { absl::MutexLock lock(mutex_); return GetArrayValue(key, default_value); } std::vector PreferencesManager::GetStringArray( absl::string_view key, absl::Span default_value) const { absl::MutexLock lock(mutex_); return GetArrayValue(key, default_value); } absl::Time PreferencesManager::GetTime(absl::string_view key, absl::Time default_value) const { absl::MutexLock lock(mutex_); auto result = value_.find(absl::StrCat(key)); if (result == value_.end()) { return default_value; } return absl::FromUnixNanos(result->get()); } bool PreferencesManager::GetProtoMessage(absl::string_view key, google::protobuf::Message* value) const { absl::MutexLock lock(mutex_); auto result = value_.find(absl::StrCat(key)); if (result == value_.end()) { return false; } return proto2::json::JsonStringToMessage(result->dump(), value) .ok(); } // Removes preferences void PreferencesManager::Remove(absl::string_view key) { absl::MutexLock lock(mutex_); value_.erase(absl::StrCat(key)); } bool PreferencesManager::RemoveKeyPrefix(absl::string_view prefix) { absl::MutexLock lock(mutex_); auto it = value_.begin(); while (it != value_.end()) { if (it.key().starts_with(prefix)) { it = value_.erase(it); } else { ++it; } } return true; } // Private methods // Writes data to storage. bool PreferencesManager::Commit() { if (!preferences_repository_->SavePreferences(value_)) { LOG(ERROR) << "Failed to save preference." << std::endl; return false; } return true; } bool PreferencesManager::SetValue(absl::string_view key, const json& value) { if (!value_.is_object()) { LOG(ERROR) << "Preferences is no longer an object! value_=" << value_.dump(4); value_ = json::object(); } if (value_[absl::StrCat(key)] == value) { return false; } value_[absl::StrCat(key)] = value; return Commit(); } template T PreferencesManager::GetValue(absl::string_view key, const T& default_value) const { if (!value_.is_object()) { LOG(ERROR) << "Preferences is no longer an object! value_=" << value_.dump(4); return default_value; } auto it = value_.find(absl::StrCat(key)); if (it == value_.end()) { return default_value; } return it->get(); } template bool PreferencesManager::SetArrayValue(absl::string_view key, absl::Span value) { if (!value_.is_object()) { LOG(ERROR) << "Preferences is no longer an object! value_=" << value_.dump(4); value_ = json::object(); } json array_value = json::array(); for (const T& item_value : value) { array_value.push_back(item_value); } if (value_[absl::StrCat(key)] == array_value) { return false; } value_[absl::StrCat(key)] = array_value; return Commit(); } template std::vector PreferencesManager::GetArrayValue( absl::string_view key, absl::Span default_value) const { std::vector result; if (!value_.is_object()) { LOG(ERROR) << "Preferences is no longer an object! value_=" << value_.dump(4); for (const T& value : default_value) { result.push_back(value); } return result; } auto array_value = value_.find(absl::StrCat(key)); if (array_value == value_.end() || !array_value->is_array()) { for (const T& value : default_value) { result.push_back(value); } return result; } auto it = array_value->begin(); while (it != array_value->end()) { result.push_back(it->get()); ++it; } return result; } } // namespace nearby::windows