Commit a23c5648 authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[remoting][mobile] Notification client

This CL implements a notification client for mobile clients to fetch
notification from gstatic. Currently it only has logic to match rules
and select a notification. It isn't connected to gstatic or any UI
component yet.

Bug: 1001291
Change-Id: I2d58b53363b1db0b9bb836dfcfb5ec2a36126d19
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1812398
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarJamie Walch <jamiewalch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#699582}
parent a734a1c3
......@@ -158,6 +158,7 @@ test("remoting_unittests") {
"//remoting/client:unit_tests",
"//remoting/client/audio:unit_tests",
"//remoting/client/input:unit_tests",
"//remoting/client/notification:unit_tests",
"//remoting/client/ui:unit_tests",
"//remoting/protocol:unit_tests",
"//remoting/signaling:unit_tests",
......
......@@ -41,6 +41,7 @@ static_library("client") {
deps = [
"//remoting/client/audio",
"//remoting/client/notification",
"//remoting/codec:decoder",
"//remoting/protocol",
"//third_party/libyuv",
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("notification") {
sources = [
"json_fetcher.h",
"notification_client.cc",
"notification_client.h",
"notification_message.cc",
"notification_message.h",
"version_range.cc",
"version_range.h",
]
deps = [
"//base",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"notification_client_unittest.cc",
"version_range_unittest.cc",
]
deps = [
":notification",
"//testing/gmock",
"//testing/gtest",
]
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_CLIENT_NOTIFICATION_JSON_FETCHER_H_
#define REMOTING_CLIENT_NOTIFICATION_JSON_FETCHER_H_
#include <string>
#include "base/callback_forward.h"
namespace base {
class Value;
} // namespace base
namespace remoting {
// Interface for fetching a JSON file under https://www.gstatic.com/chromoting.
class JsonFetcher {
public:
JsonFetcher() = default;
virtual ~JsonFetcher() = default;
// |relative_path| is relative to https://www.gstatic.com/chromoting/.
// Runs |done| with the decoded value if the file is successfully fetched,
// otherwise runs |done| with base::nullopt.
//
// Note that the implementation MUST be able to handle concurrent requests and
// MUST NOT keep |done| after its destructor is called.
virtual void FetchJsonFile(
const std::string& relative_path,
base::OnceCallback<void(base::Optional<base::Value>)> done) const = 0;
};
} // namespace remoting
#endif // REMOTING_CLIENT_NOTIFICATION_JSON_FETCHER_H_
\ No newline at end of file
This diff is collapsed.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_CLIENT_H_
#define REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_CLIENT_H_
#include <memory>
#include <string>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/optional.h"
namespace base {
class Value;
} // namespace base
namespace remoting {
class JsonFetcher;
struct NotificationMessage;
// Class for fetching a notification from the server so that the caller can
// show that on some UI component when the app is launched.
class NotificationClient final {
public:
using NotificationCallback =
base::OnceCallback<void(base::Optional<NotificationMessage>)>;
~NotificationClient();
// Fetches notifications from the server and calls |callback| with the
// best matched notification. If notifications failed to fetch or no matching
// notification is found then base::nullopt will be returned. |callback| will
// be silently dropped if |this| is deleted before the notification is
// fetched.
void GetNotification(const std::string& user_email,
NotificationCallback callback);
private:
friend class NotificationClientTest;
// Constructor for unittest.
NotificationClient(std::unique_ptr<JsonFetcher> fetcher,
const std::string& current_platform,
const std::string& current_version,
const std::string& locale);
void OnRulesFetched(const std::string& user_email,
NotificationCallback callback,
base::Optional<base::Value> rules);
// Returns non-empty NotificationMessage if the rule is parsed successfully
// and the rule should apply to the user. |message_text| and |link_text| will
// not be set and caller needs to call FetchTranslatedText to fill them up.
base::Optional<NotificationMessage> ParseAndMatchRule(
const base::Value& rule,
const std::string& user_email,
std::string* out_message_text_filename,
std::string* out_link_text_filename);
void FetchTranslatedTexts(const std::string& message_text_filename,
const std::string& link_text_filename,
base::Optional<NotificationMessage> partial_message,
NotificationCallback done);
std::unique_ptr<JsonFetcher> fetcher_;
std::string current_platform_;
std::string current_version_;
std::string locale_;
DISALLOW_COPY_AND_ASSIGN(NotificationClient);
};
} // namespace remoting
#endif // REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_CLIENT_H_
This diff is collapsed.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/client/notification/notification_message.h"
namespace remoting {
NotificationMessage::NotificationMessage() = default;
NotificationMessage::NotificationMessage(const NotificationMessage&) = default;
NotificationMessage::NotificationMessage(NotificationMessage&&) = default;
NotificationMessage::~NotificationMessage() = default;
NotificationMessage& NotificationMessage::operator=(
const NotificationMessage&) = default;
NotificationMessage& NotificationMessage::operator=(NotificationMessage&&) =
default;
} // namespace remoting
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_MESSAGE_H_
#define REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_MESSAGE_H_
#include <string>
namespace remoting {
struct NotificationMessage final {
enum class Appearance {
TOAST,
DIALOG,
};
NotificationMessage();
NotificationMessage(const NotificationMessage&);
NotificationMessage(NotificationMessage&&);
~NotificationMessage();
NotificationMessage& operator=(const NotificationMessage&);
NotificationMessage& operator=(NotificationMessage&&);
Appearance appearance;
std::string message_text;
std::string link_text;
std::string link_url;
};
} // namespace remoting
#endif // REMOTING_CLIENT_NOTIFICATION_NOTIFICATION_MESSAGE_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/client/notification/version_range.h"
#include <stdint.h>
#include <limits>
#include "base/logging.h"
namespace remoting {
namespace {
constexpr uint16_t kUnboundMinVersionNumber = 0;
constexpr uint16_t kUnboundMaxVersionNumber =
std::numeric_limits<uint16_t>::max();
} // namespace
VersionRange::VersionRange(const std::string& range_spec) {
if (range_spec.empty()) {
// Invalid.
return;
}
size_t dash_pos = range_spec.find('-');
if (dash_pos == std::string::npos) {
// May be a single version string.
min_version_ = base::make_optional<base::Version>(range_spec);
max_version_ = base::make_optional<base::Version>(*min_version_);
is_min_version_inclusive_ = true;
is_max_version_inclusive_ = true;
return;
}
char left_bracket = range_spec.front();
if (left_bracket != '[' && left_bracket != '(') {
// Invalid.
return;
}
is_min_version_inclusive_ = (left_bracket == '[');
char right_bracket = range_spec.back();
if (right_bracket != ']' && right_bracket != ')') {
// Invalid.
return;
}
is_max_version_inclusive_ = (right_bracket == ']');
DCHECK_GE(range_spec.size(), 3u);
DCHECK_GT(dash_pos, 0u);
DCHECK_LT(dash_pos, range_spec.size() - 1);
std::string min_version_string = range_spec.substr(1, dash_pos - 1);
if (min_version_string.empty()) {
// Unbound min version.
std::vector<uint32_t> version_components{kUnboundMinVersionNumber};
min_version_ =
base::make_optional<base::Version>(std::move(version_components));
} else {
min_version_ = base::make_optional<base::Version>(min_version_string);
}
std::string max_version_string = range_spec.substr(dash_pos + 1);
max_version_string.pop_back();
if (max_version_string.empty()) {
// Unbound max version.
std::vector<uint32_t> version_components{kUnboundMaxVersionNumber};
max_version_ =
base::make_optional<base::Version>(std::move(version_components));
} else {
max_version_ = base::make_optional<base::Version>(max_version_string);
}
}
VersionRange::~VersionRange() = default;
bool VersionRange::IsValid() const {
return min_version_ && min_version_->IsValid() && max_version_ &&
max_version_->IsValid() && *min_version_ <= *max_version_;
}
bool VersionRange::ContainsVersion(const std::string& version_string) const {
if (!IsValid()) {
return false;
}
base::Version version(version_string);
if (!version.IsValid()) {
LOG(ERROR) << "Invalid version number: " << version_string;
return false;
}
int min_version_compare_result = min_version_->CompareTo(version);
if (min_version_compare_result > 0 ||
(min_version_compare_result == 0 && !is_min_version_inclusive_)) {
return false;
}
int max_version_compare_result = max_version_->CompareTo(version);
if (max_version_compare_result < 0 ||
(max_version_compare_result == 0 && !is_max_version_inclusive_)) {
return false;
}
return true;
}
} // namespace remoting
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_CLIENT_NOTIFICATION_VERSION_RANGE_H_
#define REMOTING_CLIENT_NOTIFICATION_VERSION_RANGE_H_
#include <string>
#include "base/macros.h"
#include "base/optional.h"
#include "base/version.h"
namespace remoting {
// Class representing a range of dotted version numbers. Supporting parsing and
// in-range checking.
class VersionRange final {
public:
// Parses a range spec into range.
//
// Format (regex):
// ([\(\[]((\d+\.)*\d+)?-((\d+\.)*\d+)?[\)\]]|(\d+\.)*\d+)
//
// Examples:
// 1.2.3 Exactly 1.2.3
// [-1.2.3) Anything up to but not including 1.2.3
// [1.2-3) Anything between 1.2 (inclusive) and 3 (exclusive)
// (1.2-3] Anything between 1.2 (exclusive) and 3 (inclusive)
// [1.2-) 1.2 (inclusive) and higher
// [-] Anything
//
// Min version must be less than or equal to max version.
explicit VersionRange(const std::string& range_spec);
~VersionRange();
bool IsValid() const;
bool ContainsVersion(const std::string& version_string) const;
private:
base::Optional<base::Version> min_version_;
base::Optional<base::Version> max_version_;
bool is_min_version_inclusive_ = false;
bool is_max_version_inclusive_ = false;
DISALLOW_COPY_AND_ASSIGN(VersionRange);
};
} // namespace remoting
#endif // REMOTING_CLIENT_NOTIFICATION_VERSION_RANGE_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/client/notification/version_range.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
TEST(VersionRangeTest, ExactVersionMatch) {
VersionRange range("1.2.3");
ASSERT_TRUE(range.IsValid());
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_FALSE(range.ContainsVersion("1.2.1"));
ASSERT_FALSE(range.ContainsVersion("1.2.5"));
}
TEST(VersionRangeTest, UnboundMinimumExclusiveMaximum) {
VersionRange range("[-1.2.3)");
ASSERT_TRUE(range.IsValid());
ASSERT_TRUE(range.ContainsVersion("0.1"));
ASSERT_TRUE(range.ContainsVersion("1.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.2"));
ASSERT_FALSE(range.ContainsVersion("1.2.3"));
ASSERT_FALSE(range.ContainsVersion("1.2.5"));
ASSERT_FALSE(range.ContainsVersion("2"));
}
TEST(VersionRangeTest, UnboundMinimumInclusiveMaximum) {
VersionRange range("[-1.2.3]");
ASSERT_TRUE(range.IsValid());
ASSERT_TRUE(range.ContainsVersion("0.1"));
ASSERT_TRUE(range.ContainsVersion("1.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_FALSE(range.ContainsVersion("1.2.5"));
ASSERT_FALSE(range.ContainsVersion("2"));
}
TEST(VersionRangeTest, InclusiveMinimumUnboundMaximum) {
VersionRange range("[1.2.3-)");
ASSERT_TRUE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0.1"));
ASSERT_FALSE(range.ContainsVersion("1.2.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_TRUE(range.ContainsVersion("1.2.5"));
ASSERT_TRUE(range.ContainsVersion("2"));
}
TEST(VersionRangeTest, ExclusiveMinimumUnboundMaximum) {
VersionRange range("(1.2.3-)");
ASSERT_TRUE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0.1"));
ASSERT_FALSE(range.ContainsVersion("1.2.2"));
ASSERT_FALSE(range.ContainsVersion("1.2.3"));
ASSERT_TRUE(range.ContainsVersion("1.2.5"));
ASSERT_TRUE(range.ContainsVersion("2"));
}
TEST(VersionRangeTest, InclusiveMinimumExclusiveMaximum) {
VersionRange range("[1.2-3)");
ASSERT_TRUE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0.1"));
ASSERT_FALSE(range.ContainsVersion("1.1"));
ASSERT_TRUE(range.ContainsVersion("1.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_TRUE(range.ContainsVersion("2"));
ASSERT_FALSE(range.ContainsVersion("3"));
ASSERT_FALSE(range.ContainsVersion("4"));
}
TEST(VersionRangeTest, ExclusiveMinimumInclusiveMaximum) {
VersionRange range("(1.2-3]");
ASSERT_TRUE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0.1"));
ASSERT_FALSE(range.ContainsVersion("1.1"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_TRUE(range.ContainsVersion("2"));
ASSERT_TRUE(range.ContainsVersion("3"));
ASSERT_FALSE(range.ContainsVersion("3.1"));
ASSERT_FALSE(range.ContainsVersion("4"));
}
TEST(VersionRangeTest, ExclusiveMinimumExclusiveMaximum) {
VersionRange range("(1.2-3)");
ASSERT_TRUE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0.1"));
ASSERT_FALSE(range.ContainsVersion("1.1"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.2"));
ASSERT_TRUE(range.ContainsVersion("1.2.3"));
ASSERT_TRUE(range.ContainsVersion("2"));
ASSERT_FALSE(range.ContainsVersion("3"));
ASSERT_FALSE(range.ContainsVersion("3.1"));
ASSERT_FALSE(range.ContainsVersion("4"));
}
TEST(VersionRangeTest, AnyVersion1) {
VersionRange range("(-)");
ASSERT_TRUE(range.IsValid());
ASSERT_TRUE(range.ContainsVersion("1"));
ASSERT_TRUE(range.ContainsVersion("1.1"));
ASSERT_TRUE(range.ContainsVersion("2"));
ASSERT_TRUE(range.ContainsVersion("3"));
ASSERT_TRUE(range.ContainsVersion("4"));
}
TEST(VersionRangeTest, AnyVersion2) {
VersionRange range("[-]");
ASSERT_TRUE(range.IsValid());
ASSERT_TRUE(range.ContainsVersion("1"));
ASSERT_TRUE(range.ContainsVersion("1.1"));
ASSERT_TRUE(range.ContainsVersion("2"));
ASSERT_TRUE(range.ContainsVersion("3"));
ASSERT_TRUE(range.ContainsVersion("4"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError1) {
VersionRange range("");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError2) {
VersionRange range("90j3awef");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError3) {
VersionRange range("[1.2)");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError4) {
VersionRange range("[1.2-");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError5) {
VersionRange range("1.2-)");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError6) {
VersionRange range("1.2-");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError7) {
VersionRange range("-1.2");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError8) {
VersionRange range("[-1.2");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_SyntaxError9) {
VersionRange range("-1.2)");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("0"));
ASSERT_FALSE(range.ContainsVersion("1.2"));
}
TEST(VersionRangeTest, InvalidRangeSpec_MinBiggerThanMax) {
VersionRange range("[3.4-1.2)");
ASSERT_FALSE(range.IsValid());
ASSERT_FALSE(range.ContainsVersion("1.2"));
ASSERT_FALSE(range.ContainsVersion("2.0"));
ASSERT_FALSE(range.ContainsVersion("3.4"));
}
} // namespace remoting
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment