Add up next view
This CL introduces an "Up next" view in the CalendarView where users can see their upcoming events. Screenshot: http://shortn/_qA0fSSZL56 Smaller refactors include: - Reduce CalendarEventListItemViewJelly controller dependency so it can be re-used in the UpNext view. - LayoutManager resizes CalendarView scroll view to dynamically make space for the CalendarUpNextView. Bug: b:258165425 Change-Id: I478bd4a5f33c222eaec0a053ac4e949f4bd9e18f Low-Coverage-Reason: Low in existing class, just made a small refactor wrapping existing params in a struct, no new functionality added Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4013467 Commit-Queue: Sam Cackett <samcackett@google.com> Reviewed-by: Jiaming Cheng <jiamingc@chromium.org> Auto-Submit: Sam Cackett <samcackett@google.com> Cr-Commit-Position: refs/heads/main@{#1072224}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c8131acb55
commit
f4c17f031b
ash
BUILD.gnash_strings.grd
ash_strings_grd
system
time
calendar_event_list_item_view_jelly.cccalendar_event_list_item_view_jelly.hcalendar_event_list_item_view_jelly_unittest.cccalendar_event_list_view.cccalendar_model.cccalendar_model.hcalendar_model_unittest.cccalendar_up_next_view.cccalendar_up_next_view.hcalendar_up_next_view_unittest.cccalendar_view.cccalendar_view.hcalendar_view_controller.cccalendar_view_controller.hcalendar_view_unittest.cc
@ -1726,6 +1726,8 @@ component("ash") {
|
||||
"system/time/calendar_model.h",
|
||||
"system/time/calendar_month_view.cc",
|
||||
"system/time/calendar_month_view.h",
|
||||
"system/time/calendar_up_next_view.cc",
|
||||
"system/time/calendar_up_next_view.h",
|
||||
"system/time/calendar_utils.cc",
|
||||
"system/time/calendar_utils.h",
|
||||
"system/time/calendar_view.cc",
|
||||
@ -3058,6 +3060,7 @@ test("ash_unittests") {
|
||||
"system/time/calendar_month_view_unittest.cc",
|
||||
"system/time/calendar_unittest_utils.cc",
|
||||
"system/time/calendar_unittest_utils.h",
|
||||
"system/time/calendar_up_next_view_unittest.cc",
|
||||
"system/time/calendar_utils_unittest.cc",
|
||||
"system/time/calendar_view_controller_unittest.cc",
|
||||
"system/time/calendar_view_unittest.cc",
|
||||
|
@ -4199,6 +4199,10 @@ Connect your device to power.
|
||||
Open in Google Calendar
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_CALENDAR_UP_NEXT" desc="Header label for the Calendar Up next view.">
|
||||
Up next
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_STATUS_TRAY_PROGRESS_BAR_ACCESSIBLE_NAME" desc="The accessible name for the progress bar shown in the status tray.">
|
||||
Loading
|
||||
</message>
|
||||
|
1
ash/ash_strings_grd/IDS_ASH_CALENDAR_UP_NEXT.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_ASH_CALENDAR_UP_NEXT.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
05da4a226414b5fea76a977dcf45df6a3272546e
|
@ -90,7 +90,8 @@ class CalendarEventListItemDot : public views::View {
|
||||
// Creates and returns a label containing the event summary.
|
||||
views::Builder<views::Label> CreateSummaryLabel(
|
||||
const std::string& event_summary,
|
||||
const std::u16string& tooltip_text) {
|
||||
const std::u16string& tooltip_text,
|
||||
const int& max_width) {
|
||||
return views::Builder<views::Label>(
|
||||
bubble_utils::CreateLabel(
|
||||
bubble_utils::TypographyStyle::kButton1,
|
||||
@ -100,6 +101,9 @@ views::Builder<views::Label> CreateSummaryLabel(
|
||||
.SetID(kSummaryLabelID)
|
||||
.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
|
||||
.SetAutoColorReadabilityEnabled(false)
|
||||
.SetMultiLine(true)
|
||||
.SetMaxLines(1)
|
||||
.SetMaximumWidth(max_width)
|
||||
.SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL)
|
||||
.SetSubpixelRenderingEnabled(false)
|
||||
.SetTextContext(CONTEXT_CALENDAR_DATE)
|
||||
@ -126,20 +130,21 @@ views::Builder<views::Label> CreateTimeLabel(
|
||||
|
||||
CalendarEventListItemViewJelly::CalendarEventListItemViewJelly(
|
||||
CalendarViewController* calendar_view_controller,
|
||||
SelectedDateParams selected_date_params,
|
||||
google_apis::calendar::CalendarEvent event,
|
||||
const bool round_top_corners,
|
||||
const bool round_bottom_corners)
|
||||
const bool round_bottom_corners,
|
||||
const int max_width)
|
||||
: ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
|
||||
calendar_view_controller_(calendar_view_controller),
|
||||
selected_date_params_(selected_date_params),
|
||||
event_url_(event.html_link()) {
|
||||
SetLayoutManager(std::make_unique<views::FillLayout>());
|
||||
|
||||
DCHECK(calendar_view_controller_->selected_date().has_value());
|
||||
|
||||
const auto [start_time, end_time] = calendar_utils::GetStartAndEndTime(
|
||||
&event, calendar_view_controller->selected_date().value(),
|
||||
calendar_view_controller->selected_date_midnight(),
|
||||
calendar_view_controller->selected_date_midnight_utc());
|
||||
&event, selected_date_params_.selected_date,
|
||||
selected_date_params_.selected_date_midnight,
|
||||
selected_date_params_.selected_date_midnight_utc);
|
||||
const auto [start_time_accessible_name, end_time_accessible_name] =
|
||||
event_date_formatter_util::GetStartAndEndTimeAccessibleNames(start_time,
|
||||
end_time);
|
||||
@ -169,8 +174,8 @@ CalendarEventListItemViewJelly::CalendarEventListItemViewJelly(
|
||||
std::u16string formatted_time_text;
|
||||
if (calendar_utils::IsMultiDayEvent(&event) || event.all_day_event()) {
|
||||
formatted_time_text = event_date_formatter_util::GetMultiDayText(
|
||||
&event, calendar_view_controller->selected_date_midnight(),
|
||||
calendar_view_controller->selected_date_midnight_utc());
|
||||
&event, selected_date_params_.selected_date_midnight,
|
||||
selected_date_params_.selected_date_midnight_utc);
|
||||
} else {
|
||||
formatted_time_text =
|
||||
event_date_formatter_util::GetFormattedInterval(start_time, end_time);
|
||||
@ -188,7 +193,8 @@ CalendarEventListItemViewJelly::CalendarEventListItemViewJelly(
|
||||
views::Builder<views::View>()
|
||||
.SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical))
|
||||
.AddChild(CreateSummaryLabel(event.summary(), tooltip_text))
|
||||
.AddChild(CreateSummaryLabel(event.summary(), tooltip_text,
|
||||
max_width))
|
||||
.AddChild(CreateTimeLabel(formatted_time_text, tooltip_text)))
|
||||
.Build());
|
||||
}
|
||||
@ -209,10 +215,9 @@ bool CalendarEventListItemViewJelly::PerformAction(const ui::Event& event) {
|
||||
|
||||
GURL finalized_url;
|
||||
bool opened_pwa = false;
|
||||
DCHECK(calendar_view_controller_->selected_date().has_value());
|
||||
Shell::Get()->system_tray_model()->client()->ShowCalendarEvent(
|
||||
event_url_, calendar_view_controller_->selected_date_midnight(),
|
||||
opened_pwa, finalized_url);
|
||||
event_url_, selected_date_params_.selected_date_midnight, opened_pwa,
|
||||
finalized_url);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,12 @@ constexpr int kTimeLabelID = 101;
|
||||
|
||||
class CalendarViewController;
|
||||
|
||||
struct SelectedDateParams {
|
||||
base::Time selected_date;
|
||||
base::Time selected_date_midnight;
|
||||
base::Time selected_date_midnight_utc;
|
||||
};
|
||||
|
||||
// This view displays a jelly version of a calendar event entry.
|
||||
class ASH_EXPORT CalendarEventListItemViewJelly : public ActionableView {
|
||||
public:
|
||||
@ -30,9 +36,11 @@ class ASH_EXPORT CalendarEventListItemViewJelly : public ActionableView {
|
||||
|
||||
CalendarEventListItemViewJelly(
|
||||
CalendarViewController* calendar_view_controller,
|
||||
SelectedDateParams selected_date_params,
|
||||
google_apis::calendar::CalendarEvent event,
|
||||
const bool round_top_corners,
|
||||
const bool round_bottom_corners);
|
||||
const bool round_bottom_corners,
|
||||
const int max_width = 0);
|
||||
CalendarEventListItemViewJelly(const CalendarEventListItemViewJelly& other) =
|
||||
delete;
|
||||
CalendarEventListItemViewJelly& operator=(
|
||||
@ -51,6 +59,8 @@ class ASH_EXPORT CalendarEventListItemViewJelly : public ActionableView {
|
||||
// Unowned.
|
||||
CalendarViewController* const calendar_view_controller_;
|
||||
|
||||
const SelectedDateParams selected_date_params_;
|
||||
|
||||
// The URL for the meeting event.
|
||||
const GURL event_url_;
|
||||
};
|
||||
|
@ -53,13 +53,18 @@ class CalendarViewEventListItemViewJellyTest : public AshTestBase {
|
||||
void CreateEventListItemView(base::Time date,
|
||||
google_apis::calendar::CalendarEvent* event,
|
||||
bool round_top_corners = false,
|
||||
bool round_bottom_corners = false) {
|
||||
bool round_bottom_corners = false,
|
||||
int max_width = 0) {
|
||||
event_list_item_view_jelly_.reset();
|
||||
controller_->UpdateMonth(date);
|
||||
controller_->selected_date_ = date;
|
||||
event_list_item_view_jelly_ =
|
||||
std::make_unique<CalendarEventListItemViewJelly>(
|
||||
controller_.get(), *event, round_top_corners, round_bottom_corners);
|
||||
controller_.get(),
|
||||
SelectedDateParams{controller_->selected_date().value(),
|
||||
controller_->selected_date_midnight(),
|
||||
controller_->selected_date_midnight_utc()},
|
||||
*event, round_top_corners, round_bottom_corners, max_width);
|
||||
}
|
||||
|
||||
void SetSelectedDateInController(base::Time date) {
|
||||
@ -167,4 +172,28 @@ TEST_F(CalendarViewEventListItemViewJellyTest, AllRoundedCorners) {
|
||||
background_layer->rounded_corner_radii());
|
||||
}
|
||||
|
||||
TEST_F(CalendarViewEventListItemViewJellyTest, MaxLabelWidth) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("22 Nov 2021 00:00 UTC", &date));
|
||||
SetSelectedDateInController(date);
|
||||
const char* start_time_string = "22 Nov 2021 09:00 GMT";
|
||||
const char* end_time_string = "22 Nov 2021 10:00 GMT";
|
||||
const auto event = CreateEvent(start_time_string, end_time_string);
|
||||
|
||||
// If we don't set `max_width`, it should default to 0 (which the
|
||||
// `views::Label`) will ignore).
|
||||
CreateEventListItemView(date, event.get(), /*round_top_corners*/ true,
|
||||
/*round_bottom_corners*/ true);
|
||||
|
||||
EXPECT_EQ(GetSummaryLabel()->GetMaximumWidth(), 0);
|
||||
|
||||
// If we set a `max_width`, it should exist on the Summary Label.
|
||||
const auto max_width = 200;
|
||||
CreateEventListItemView(date, event.get(), /*round_top_corners*/ true,
|
||||
/*round_bottom_corners*/ true,
|
||||
/*max_width=*/max_width);
|
||||
|
||||
EXPECT_EQ(GetSummaryLabel()->GetMaximumWidth(), 200);
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -232,7 +232,12 @@ std::unique_ptr<views::View> CalendarEventListView::CreateChildEventListView(
|
||||
++it) {
|
||||
container->AddChildView(std::make_unique<CalendarEventListItemViewJelly>(
|
||||
/*calendar_view_controller=*/calendar_view_controller_,
|
||||
/*event=*/*it,
|
||||
/*selected_date_params=*/
|
||||
SelectedDateParams{
|
||||
calendar_view_controller_->selected_date().value(),
|
||||
calendar_view_controller_->selected_date_midnight(),
|
||||
calendar_view_controller_->selected_date_midnight_utc()}, /*event=*/
|
||||
*it,
|
||||
/*round_top_corners=*/it == events.begin(),
|
||||
/*round_bottom_corners=*/it->id() == events.rbegin()->id()));
|
||||
}
|
||||
|
@ -89,6 +89,45 @@ void SortByDateAscending(
|
||||
});
|
||||
}
|
||||
|
||||
bool EventStartsInTenMins(const CalendarEvent& event,
|
||||
const base::Time& now_local) {
|
||||
const int start_time_difference_in_mins =
|
||||
(ash::calendar_utils::GetStartTimeAdjusted(&event) - now_local)
|
||||
.InMinutes();
|
||||
|
||||
return (0 <= start_time_difference_in_mins &&
|
||||
start_time_difference_in_mins <= 10);
|
||||
}
|
||||
|
||||
bool EventStartedLessThanOneHourAgo(const CalendarEvent& event,
|
||||
const base::Time& now_local) {
|
||||
const int start_time_difference_in_mins =
|
||||
(ash::calendar_utils::GetStartTimeAdjusted(&event) - now_local)
|
||||
.InMinutes();
|
||||
const int end_time_difference_in_mins =
|
||||
(ash::calendar_utils::GetEndTimeAdjusted(&event) - now_local).InMinutes();
|
||||
|
||||
return (0 <= end_time_difference_in_mins &&
|
||||
0 > start_time_difference_in_mins &&
|
||||
start_time_difference_in_mins >= -60);
|
||||
}
|
||||
|
||||
// Returns events that start in 10 minutes time, or events that are in progress
|
||||
// and started less than one hour ago.
|
||||
auto FilterEventsStartingSoonOrRecentlyInProgress(
|
||||
const ash::SingleDayEventList& list,
|
||||
const base::Time& now_local) {
|
||||
std::list<CalendarEvent> result;
|
||||
|
||||
for (const CalendarEvent& event : list) {
|
||||
if (EventStartsInTenMins(event, now_local) ||
|
||||
EventStartedLessThanOneHourAgo(event, now_local))
|
||||
result.emplace_back(event);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ash {
|
||||
@ -479,6 +518,12 @@ CalendarModel::FindEventsSplitByMultiDayAndSameDay(base::Time day) const {
|
||||
return SplitEventsIntoMultiDayAndSameDay(FindEvents(day));
|
||||
}
|
||||
|
||||
std::list<CalendarEvent> CalendarModel::FindUpcomingEvents(
|
||||
base::Time now_local) const {
|
||||
return FilterEventsStartingSoonOrRecentlyInProgress(FindEvents(now_local),
|
||||
now_local);
|
||||
}
|
||||
|
||||
CalendarModel::FetchingStatus CalendarModel::FindFetchingStatus(
|
||||
base::Time start_time) const {
|
||||
if (!calendar_utils::ShouldFetchEvents())
|
||||
|
@ -121,6 +121,11 @@ class ASH_EXPORT CalendarModel : public SessionObserver {
|
||||
std::tuple<SingleDayEventList, SingleDayEventList>
|
||||
FindEventsSplitByMultiDayAndSameDay(base::Time day) const;
|
||||
|
||||
// Uses the `FindEvents` method to get events for that day and then filters
|
||||
// the result into events that start or end in the next two hours.
|
||||
std::list<google_apis::calendar::CalendarEvent> FindUpcomingEvents(
|
||||
base::Time now_local) const;
|
||||
|
||||
// Checks the `FetchingStatus` of a given start time.
|
||||
FetchingStatus FindFetchingStatus(base::Time start_time) const;
|
||||
|
||||
@ -139,6 +144,8 @@ class ASH_EXPORT CalendarModel : public SessionObserver {
|
||||
friend class CalendarViewAnimationTest;
|
||||
friend class CalendarViewEventListViewTest;
|
||||
friend class CalendarViewTest;
|
||||
friend class CalendarViewWithJellyEnabledTest;
|
||||
friend class CalendarUpNextViewTest;
|
||||
friend class GlanceablesTest;
|
||||
|
||||
// Checks if the event has allowed statuses and is eligible for insertion.
|
||||
|
@ -1243,4 +1243,78 @@ TEST_F(CalendarModelTest, FindEventsSplitByMultiDayAndSameDay) {
|
||||
EXPECT_EQ(same_day_events.back().id(), kSameDayId);
|
||||
}
|
||||
|
||||
TEST_F(CalendarModelTest, FindUpcomingEvents) {
|
||||
// Set timezone and fake now.
|
||||
const char* kNow = "10 Nov 2022 13:00 GMT";
|
||||
ash::system::ScopedTimezoneSettings timezone_settings(u"GMT");
|
||||
SetTodayFromStr(kNow);
|
||||
|
||||
const char* kSummary = "summary";
|
||||
const char* kEventStartingInTenMinsId = "event_starting_in_ten_mins";
|
||||
const char* kEventStartingInThirtyMinsId = "event_starting_in_thirty_mins";
|
||||
const char* kEventStartingInTwoHoursId = "event_starting_in_two_hours";
|
||||
const char* kEventInProgressStartedLessThanOneHourAgoId =
|
||||
"event_in_progress_started_less_than_one_hour_ago";
|
||||
const char* kEventInProgressStartedMoreThanOneHourAgoId =
|
||||
"event_in_progress_started_more_than_one_hour_ago";
|
||||
const char* kEventFinishedId = "event_finished";
|
||||
|
||||
auto event_starting_in_ten_mins = calendar_test_utils::CreateEvent(
|
||||
kEventStartingInTenMinsId, kSummary, "10 Nov 2022 13:10 GMT",
|
||||
"10 Nov 2022 15:00 GMT");
|
||||
auto event_starting_in_thirty_mins = calendar_test_utils::CreateEvent(
|
||||
kEventStartingInThirtyMinsId, kSummary, "10 Nov 2022 13:30 GMT",
|
||||
"10 Nov 2022 15:00 GMT");
|
||||
auto event_starting_in_two_hours = calendar_test_utils::CreateEvent(
|
||||
kEventStartingInTwoHoursId, kSummary, "10 Nov 2022 15:00 GMT",
|
||||
"10 Nov 2022 16:00 GMT");
|
||||
auto event_in_progress_started_less_than_one_hour_ago =
|
||||
calendar_test_utils::CreateEvent(
|
||||
kEventInProgressStartedLessThanOneHourAgoId, kSummary,
|
||||
"10 Nov 2022 12:01:00 GMT", "10 Nov 2022 17:00 GMT");
|
||||
auto event_in_progress_started_more_than_one_hour_ago =
|
||||
calendar_test_utils::CreateEvent(
|
||||
kEventInProgressStartedMoreThanOneHourAgoId, kSummary,
|
||||
"10 Nov 2022 11:00 GMT", "10 Nov 2022 17:00 GMT");
|
||||
auto event_finished = calendar_test_utils::CreateEvent(
|
||||
kEventFinishedId, kSummary, "10 Nov 2022 12:30 GMT",
|
||||
"10 Nov 2022 12:59 GMT");
|
||||
|
||||
// Prepare mock events list.
|
||||
std::unique_ptr<google_apis::calendar::EventList> event_list =
|
||||
std::make_unique<google_apis::calendar::EventList>();
|
||||
event_list->InjectItemForTesting(std::move(event_starting_in_ten_mins));
|
||||
event_list->InjectItemForTesting(std::move(event_starting_in_thirty_mins));
|
||||
event_list->InjectItemForTesting(std::move(event_starting_in_two_hours));
|
||||
event_list->InjectItemForTesting(
|
||||
std::move(event_in_progress_started_less_than_one_hour_ago));
|
||||
event_list->InjectItemForTesting(
|
||||
std::move(event_in_progress_started_more_than_one_hour_ago));
|
||||
event_list->InjectItemForTesting(std::move(event_finished));
|
||||
|
||||
// Mock the events are fetched.
|
||||
MockOnEventsFetched(calendar_utils::GetStartOfMonthUTC(
|
||||
calendar_test_utils::GetTimeFromString(kNow)),
|
||||
google_apis::ApiErrorCode::HTTP_SUCCESS,
|
||||
event_list.get());
|
||||
|
||||
auto events = calendar_model_->FindUpcomingEvents(now_);
|
||||
|
||||
auto event_list_contains = [](auto& event_list, auto& id) {
|
||||
return base::Contains(event_list, id, &CalendarEvent::id);
|
||||
};
|
||||
|
||||
// We should only get the 2 events back that start in 10 mins or were ongoing
|
||||
// with < 60 mins passed.
|
||||
EXPECT_EQ(events.size(), size_t(2));
|
||||
EXPECT_TRUE(event_list_contains(events, kEventStartingInTenMinsId));
|
||||
EXPECT_FALSE(event_list_contains(events, kEventStartingInThirtyMinsId));
|
||||
EXPECT_FALSE(event_list_contains(events, kEventStartingInTwoHoursId));
|
||||
EXPECT_TRUE(
|
||||
event_list_contains(events, kEventInProgressStartedLessThanOneHourAgoId));
|
||||
EXPECT_FALSE(
|
||||
event_list_contains(events, kEventInProgressStartedMoreThanOneHourAgoId));
|
||||
EXPECT_FALSE(event_list_contains(events, kEventFinishedId));
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
113
ash/system/time/calendar_up_next_view.cc
Normal file
113
ash/system/time/calendar_up_next_view.cc
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2022 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/time/calendar_up_next_view.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/bubble/bubble_utils.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/time/calendar_event_list_item_view_jelly.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
#include "ui/color/color_provider.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/controls/scroll_view.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
#include "ui/views/layout/flex_layout.h"
|
||||
#include "ui/views/metadata/view_factory_internal.h"
|
||||
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
constexpr int kContainerInsets = 12;
|
||||
constexpr int kBackgroundRadius = 12;
|
||||
constexpr int kBetweenChildSpacing = 8;
|
||||
constexpr int kMaxEventListItemWidth = 160;
|
||||
|
||||
views::Builder<views::Label> CreateHeaderLabel() {
|
||||
return views::Builder<views::Label>(bubble_utils::CreateLabel(
|
||||
bubble_utils::TypographyStyle::kButton2,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_UP_NEXT)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CalendarUpNextView::CalendarUpNextView(
|
||||
CalendarViewController* calendar_view_controller)
|
||||
: calendar_view_controller_(calendar_view_controller),
|
||||
header_view_(AddChildView(std::make_unique<views::View>())),
|
||||
scroll_view_(AddChildView(std::make_unique<views::ScrollView>(
|
||||
views::ScrollView::ScrollWithLayers::kEnabled))),
|
||||
content_view_(
|
||||
scroll_view_->SetContents(std::make_unique<views::View>())) {
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical, gfx::Insets(kContainerInsets),
|
||||
kBetweenChildSpacing));
|
||||
|
||||
header_view_->SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||
header_view_->AddChildView(CreateHeaderLabel().Build());
|
||||
|
||||
scroll_view_->SetAllowKeyboardScrolling(false);
|
||||
scroll_view_->SetBackgroundColor(absl::nullopt);
|
||||
scroll_view_->SetDrawOverflowIndicator(false);
|
||||
scroll_view_->SetHorizontalScrollBarMode(
|
||||
views::ScrollView::ScrollBarMode::kHiddenButEnabled);
|
||||
scroll_view_->SetTreatAllScrollEventsAsHorizontal(true);
|
||||
|
||||
content_view_->SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
|
||||
kBetweenChildSpacing));
|
||||
|
||||
UpdateEvents();
|
||||
}
|
||||
|
||||
CalendarUpNextView::~CalendarUpNextView() = default;
|
||||
|
||||
void CalendarUpNextView::Layout() {
|
||||
// For some reason the `content_view_` is constrained to the `scroll_view_`
|
||||
// width and so it isn't scrollable. This seems to be a problem with
|
||||
// horizontal `ScrollView`s as this doesn't happen if you make this view
|
||||
// vertically scrollable. To make the content scrollable, we need to set it's
|
||||
// preferred size here so it's bigger than the `scroll_view_` and
|
||||
// therefore scrolls.
|
||||
if (content_view_)
|
||||
content_view_->SizeToPreferredSize();
|
||||
|
||||
// `content_view_` is a child of this class so we need to Layout after
|
||||
// changing its width.
|
||||
views::View::Layout();
|
||||
}
|
||||
|
||||
void CalendarUpNextView::OnThemeChanged() {
|
||||
views::View::OnThemeChanged();
|
||||
SetBackground(views::CreateRoundedRectBackground(
|
||||
GetColorProvider()->GetColor(cros_tokens::kCrosSysSystemOnBase),
|
||||
kBackgroundRadius));
|
||||
}
|
||||
|
||||
void CalendarUpNextView::UpdateEvents() {
|
||||
content_view_->RemoveAllChildViews();
|
||||
|
||||
std::list<google_apis::calendar::CalendarEvent> events =
|
||||
calendar_view_controller_->UpcomingEvents();
|
||||
|
||||
auto now = base::Time::NowFromSystemTime();
|
||||
for (auto& event : events) {
|
||||
content_view_->AddChildView(
|
||||
std::make_unique<CalendarEventListItemViewJelly>(
|
||||
calendar_view_controller_,
|
||||
SelectedDateParams{now, now.UTCMidnight(), now.LocalMidnight()},
|
||||
/*event=*/event, /*round_top_corners=*/true,
|
||||
/*round_bottom_corners=*/true,
|
||||
/*max_width=*/kMaxEventListItemWidth));
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_METADATA(CalendarUpNextView, views::View);
|
||||
END_METADATA
|
||||
|
||||
} // namespace ash
|
48
ash/system/time/calendar_up_next_view.h
Normal file
48
ash/system/time/calendar_up_next_view.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2022 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_H_
|
||||
#define ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_H_
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/system/time/calendar_view_controller.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// This view displays a scrollable list of `CalendarEventListItemView` for the
|
||||
// events that a user has coming up, either imminently or that are already in
|
||||
// progress but not yet finished.
|
||||
class ASH_EXPORT CalendarUpNextView : public views::View {
|
||||
public:
|
||||
METADATA_HEADER(CalendarUpNextView);
|
||||
|
||||
explicit CalendarUpNextView(CalendarViewController* calendar_view_controller);
|
||||
CalendarUpNextView(const CalendarUpNextView& other) = delete;
|
||||
CalendarUpNextView& operator=(const CalendarUpNextView& other) = delete;
|
||||
~CalendarUpNextView() override;
|
||||
|
||||
// views::View
|
||||
void OnThemeChanged() override;
|
||||
void Layout() override;
|
||||
|
||||
private:
|
||||
friend class CalendarUpNextViewTest;
|
||||
|
||||
void UpdateEvents();
|
||||
|
||||
// Owned by `CalendarView`.
|
||||
CalendarViewController* calendar_view_controller_;
|
||||
|
||||
// Owned by `CalendarUpNextView`.
|
||||
views::View* const header_view_;
|
||||
views::ScrollView* const scroll_view_;
|
||||
// The content of the horizontal `scroll_view`, which carries a list of
|
||||
// `CalendarEventListItemView`.
|
||||
views::View* const content_view_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_H_
|
129
ash/system/time/calendar_up_next_view_unittest.cc
Normal file
129
ash/system/time/calendar_up_next_view_unittest.cc
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2022 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/time/calendar_up_next_view.h"
|
||||
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/model/system_tray_model.h"
|
||||
#include "ash/system/time/calendar_unittest_utils.h"
|
||||
#include "ash/system/time/calendar_view_controller.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/time/time.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
|
||||
const char* start_time,
|
||||
const char* end_time,
|
||||
bool all_day_event = false) {
|
||||
return calendar_test_utils::CreateEvent(
|
||||
"id_0", "summary_0", start_time, end_time,
|
||||
google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
|
||||
google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
|
||||
all_day_event);
|
||||
}
|
||||
|
||||
std::unique_ptr<google_apis::calendar::EventList> CreateMockEventList(
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>>& events) {
|
||||
auto event_list = std::make_unique<google_apis::calendar::EventList>();
|
||||
event_list->set_time_zone("Greenwich Mean Time");
|
||||
|
||||
for (auto& event : events)
|
||||
event_list->InjectItemForTesting(std::move(event));
|
||||
|
||||
return event_list;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class CalendarUpNextViewTest : public AshTestBase {
|
||||
public:
|
||||
CalendarUpNextViewTest() = default;
|
||||
CalendarUpNextViewTest(const CalendarUpNextViewTest&) = delete;
|
||||
CalendarUpNextViewTest& operator=(const CalendarUpNextViewTest&) = delete;
|
||||
~CalendarUpNextViewTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
AshTestBase::SetUp();
|
||||
controller_ = std::make_unique<CalendarViewController>();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
up_next_view_.reset();
|
||||
controller_.reset();
|
||||
AshTestBase::TearDown();
|
||||
}
|
||||
|
||||
void CreateUpNextView(
|
||||
base::Time date,
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>>&
|
||||
events) {
|
||||
up_next_view_.reset();
|
||||
Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched(
|
||||
calendar_utils::GetStartOfMonthUTC(date),
|
||||
google_apis::ApiErrorCode::HTTP_SUCCESS,
|
||||
CreateMockEventList(events).get());
|
||||
up_next_view_ = std::make_unique<CalendarUpNextView>(controller_.get());
|
||||
}
|
||||
|
||||
const views::Label* GetHeaderLabel() {
|
||||
return static_cast<views::Label*>(
|
||||
up_next_view_->header_view_->children()[0]);
|
||||
}
|
||||
|
||||
const views::View* GetContentsView() {
|
||||
return static_cast<views::View*>(up_next_view_->content_view_);
|
||||
}
|
||||
|
||||
static base::Time FakeTimeNow() { return fake_time_; }
|
||||
static void SetFakeNow(base::Time fake_now) { fake_time_ = fake_now; }
|
||||
|
||||
CalendarViewController* controller() { return controller_.get(); }
|
||||
|
||||
CalendarUpNextView* up_next_view() { return up_next_view_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<CalendarUpNextView> up_next_view_;
|
||||
std::unique_ptr<CalendarViewController> controller_;
|
||||
base::test::ScopedFeatureList features_;
|
||||
static base::Time fake_time_;
|
||||
};
|
||||
|
||||
base::Time CalendarUpNextViewTest::fake_time_;
|
||||
|
||||
TEST_F(CalendarUpNextViewTest,
|
||||
GivenUpcomingEvents_WhenUpNextViewIsCreated_ThenShowEvents) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("22 Nov 2021 09:00 GMT", &date));
|
||||
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarUpNextViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
// Event starts in 10 mins.
|
||||
const char* event_in_ten_mins_start_time_string = "22 Nov 2021 09:10 GMT";
|
||||
const char* event_in_ten_mins_end_time_string = "22 Nov 2021 10:00 GMT";
|
||||
// Event in progress.
|
||||
const char* event_in_progress_start_time_string = "22 Nov 2021 08:30 GMT";
|
||||
const char* event_in_progress_end_time_string = "22 Nov 2021 09:30 GMT";
|
||||
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events;
|
||||
events.emplace_back(CreateEvent(event_in_ten_mins_start_time_string,
|
||||
event_in_ten_mins_end_time_string));
|
||||
events.emplace_back(CreateEvent(event_in_progress_start_time_string,
|
||||
event_in_progress_end_time_string));
|
||||
|
||||
CreateUpNextView(date, events);
|
||||
|
||||
EXPECT_EQ(GetHeaderLabel()->GetText(), u"Up next");
|
||||
EXPECT_EQ(GetContentsView()->children().size(), size_t(2));
|
||||
}
|
||||
|
||||
} // namespace ash
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/public/cpp/ash_typography.h"
|
||||
#include "ash/resources/vector_icons/vector_icons.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
@ -89,6 +90,9 @@ constexpr base::TimeDelta kAnimationDurationForClosingEvents =
|
||||
// The cool-down time for enabling animation.
|
||||
constexpr base::TimeDelta kAnimationDisablingTimeout = base::Milliseconds(500);
|
||||
|
||||
// Periodic time delay for checking upcoming events.
|
||||
constexpr base::TimeDelta kCheckUpcomingEventsDelay = base::Seconds(15);
|
||||
|
||||
// The multiplier used to reduce velocity of flings on the calendar view.
|
||||
// Without this, CalendarView will scroll a few years per fast swipe.
|
||||
constexpr float kCalendarScrollFlingMultiplier = 0.25f;
|
||||
@ -371,6 +375,8 @@ CalendarView::CalendarView(DetailedViewDelegate* delegate,
|
||||
calendar_view->set_should_months_animate(true);
|
||||
},
|
||||
base::Unretained(this))) {
|
||||
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical));
|
||||
SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||
|
||||
// Focusable nodes must have an accessible name and valid role.
|
||||
@ -443,6 +449,8 @@ CalendarView::CalendarView(DetailedViewDelegate* delegate,
|
||||
|
||||
// Add scroll view.
|
||||
scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
|
||||
// Flex the scrollview around any sibling views that are added or removed.
|
||||
layout->SetFlexForView(scroll_view_, 1);
|
||||
scroll_view_->SetAllowKeyboardScrolling(false);
|
||||
scroll_view_->SetBackgroundColor(absl::nullopt);
|
||||
scroll_view_->ClipHeightTo(0, INT_MAX);
|
||||
@ -483,6 +491,11 @@ CalendarView::CalendarView(DetailedViewDelegate* delegate,
|
||||
scoped_view_observer_.AddObservation(scroll_view_);
|
||||
scoped_view_observer_.AddObservation(content_view_);
|
||||
scoped_view_observer_.AddObservation(this);
|
||||
|
||||
check_upcoming_events_timer_.Start(
|
||||
FROM_HERE, kCheckUpcomingEventsDelay,
|
||||
base::BindRepeating(&CalendarView::MaybeShowUpNextView,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
CalendarView::~CalendarView() {
|
||||
@ -497,6 +510,8 @@ CalendarView::~CalendarView() {
|
||||
RemoveChildViewT(event_list_view_);
|
||||
event_list_view_ = nullptr;
|
||||
}
|
||||
check_upcoming_events_timer_.Stop();
|
||||
RemoveUpNextView();
|
||||
content_view_->RemoveAllChildViews();
|
||||
}
|
||||
|
||||
@ -712,17 +727,20 @@ void CalendarView::UpdateOnScreenMonthMap() {
|
||||
MaybeUpdateLoadingBarVisibility();
|
||||
}
|
||||
|
||||
void CalendarView::MaybeUpdateLoadingBarVisibility() {
|
||||
bool CalendarView::EventsFetchComplete() {
|
||||
for (auto& it : on_screen_month_) {
|
||||
// If there's an on-screen month that hasn't finished fetching or
|
||||
// re-fetching, the loading bar should be visible.
|
||||
// Return false if there's an on-screen month that hasn't finished fetching
|
||||
// or re-fetching.
|
||||
if (it.second == CalendarModel::kFetching ||
|
||||
it.second == CalendarModel::kRefetching) {
|
||||
ShowProgress(-1, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ShowProgress(-1, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CalendarView::MaybeUpdateLoadingBarVisibility() {
|
||||
ShowProgress(-1, !EventsFetchComplete());
|
||||
}
|
||||
|
||||
void CalendarView::FadeInCurrentMonth() {
|
||||
@ -995,6 +1013,11 @@ void CalendarView::OnEventsFetched(
|
||||
on_screen_month_[start_time] = status;
|
||||
|
||||
MaybeUpdateLoadingBarVisibility();
|
||||
|
||||
// Only show up next for events that are the same month as `base::Time::Now`.
|
||||
if (start_time == calendar_utils::GetStartOfMonthUTC(
|
||||
base::Time::NowFromSystemTime().UTCMidnight()))
|
||||
MaybeShowUpNextView();
|
||||
}
|
||||
|
||||
void CalendarView::OnTimeout(const base::Time start_time) {
|
||||
@ -1799,6 +1822,30 @@ void CalendarView::SetEventListViewBounds() {
|
||||
kEventListViewVerticalPadding);
|
||||
}
|
||||
|
||||
void CalendarView::MaybeShowUpNextView() {
|
||||
if (features::IsCalendarJellyEnabled() && EventsFetchComplete() &&
|
||||
!calendar_view_controller_->UpcomingEvents().empty()) {
|
||||
if (up_next_view_)
|
||||
return;
|
||||
|
||||
up_next_view_ = AddChildView(
|
||||
std::make_unique<CalendarUpNextView>(calendar_view_controller_.get()));
|
||||
} else {
|
||||
RemoveUpNextView();
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarView::RemoveUpNextView() {
|
||||
if (up_next_view_) {
|
||||
RemoveChildViewT(up_next_view_);
|
||||
up_next_view_ = nullptr;
|
||||
// If the up next view is deleted whilst the calendar is still open, e.g.
|
||||
// time has passed and an event no longer meets 'upcoming' criteria, then
|
||||
// the calendar view needs to relayout after removing the upnext view.
|
||||
InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_METADATA(CalendarView, views::View)
|
||||
END_METADATA
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/model/system_tray_model.h"
|
||||
#include "ash/system/time/calendar_model.h"
|
||||
#include "ash/system/time/calendar_up_next_view.h"
|
||||
#include "ash/system/time/calendar_view_controller.h"
|
||||
#include "ash/system/tray/tray_detailed_view.h"
|
||||
#include "ash/system/unified/unified_system_tray_controller.h"
|
||||
@ -231,6 +232,9 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
// Updates the on-screen month map with the current months on screen.
|
||||
void UpdateOnScreenMonthMap();
|
||||
|
||||
// Returns whether or not we've finished fetching CalendarEvents.
|
||||
bool EventsFetchComplete();
|
||||
|
||||
// Checks if all months in the visible window have finished fetching. If so,
|
||||
// stop showing the loading bar.
|
||||
void MaybeUpdateLoadingBarVisibility();
|
||||
@ -324,6 +328,12 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
// bounds.
|
||||
void SetEventListViewBounds();
|
||||
|
||||
// Conditionally displays the "Up next" view.
|
||||
void MaybeShowUpNextView();
|
||||
|
||||
// Removes the "Up next" view.
|
||||
void RemoveUpNextView();
|
||||
|
||||
// Setters for animation flags.
|
||||
void set_should_header_animate(bool should_animate) {
|
||||
should_header_animate_ = should_animate;
|
||||
@ -362,6 +372,7 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
IconButton* up_button_ = nullptr;
|
||||
IconButton* down_button_ = nullptr;
|
||||
CalendarEventListView* event_list_view_ = nullptr;
|
||||
CalendarUpNextView* up_next_view_ = nullptr;
|
||||
std::map<base::Time, CalendarModel::FetchingStatus> on_screen_month_;
|
||||
CalendarModel* calendar_model_ =
|
||||
Shell::Get()->system_tray_model()->calendar_model();
|
||||
@ -399,6 +410,9 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
base::RetainingOneShotTimer header_animation_restart_timer_;
|
||||
base::RetainingOneShotTimer months_animation_restart_timer_;
|
||||
|
||||
// Timer that checks upcoming events periodically.
|
||||
base::RepeatingTimer check_upcoming_events_timer_;
|
||||
|
||||
base::CallbackListSubscription on_contents_scrolled_subscription_;
|
||||
base::ScopedObservation<CalendarModel, CalendarModel::Observer>
|
||||
scoped_calendar_model_observer_{this};
|
||||
|
@ -158,6 +158,14 @@ CalendarViewController::SelectedDateEventsSplitByMultiDayAndSameDay() {
|
||||
ApplyTimeDifference(selected_date_.value()));
|
||||
}
|
||||
|
||||
SingleDayEventList CalendarViewController::UpcomingEvents() {
|
||||
return Shell::Get()
|
||||
->system_tray_model()
|
||||
->calendar_model()
|
||||
->FindUpcomingEvents(
|
||||
ApplyTimeDifference(base::Time::NowFromSystemTime()));
|
||||
}
|
||||
|
||||
int CalendarViewController::GetEventNumber(base::Time date) {
|
||||
return Shell::Get()->system_tray_model()->calendar_model()->EventsNumberOfDay(
|
||||
ApplyTimeDifference(date),
|
||||
|
@ -116,6 +116,9 @@ class ASH_EXPORT CalendarViewController {
|
||||
std::tuple<SingleDayEventList, SingleDayEventList>
|
||||
SelectedDateEventsSplitByMultiDayAndSameDay();
|
||||
|
||||
// Returns upcoming events for the "Up next" view.
|
||||
SingleDayEventList UpcomingEvents();
|
||||
|
||||
// The calendar events number of the `date`.
|
||||
int GetEventNumber(base::Time date);
|
||||
|
||||
|
@ -189,6 +189,8 @@ class CalendarViewTest : public AshTestBase {
|
||||
}
|
||||
views::View* event_list_view() { return calendar_view_->event_list_view_; }
|
||||
|
||||
views::View* up_next_view() { return calendar_view_->up_next_view_; }
|
||||
|
||||
void ScrollUpOneMonth() {
|
||||
calendar_view_->ScrollOneMonthAndAutoScroll(/*scroll_up=*/true);
|
||||
}
|
||||
@ -2142,4 +2144,144 @@ TEST_F(CalendarViewWithMessageCenterTest,
|
||||
EXPECT_EQ(current_date_cell_view, calendar_focus_manager()->GetFocusedView());
|
||||
}
|
||||
|
||||
class CalendarViewWithJellyEnabledTest : public CalendarViewTest {
|
||||
public:
|
||||
CalendarViewWithJellyEnabledTest() = default;
|
||||
CalendarViewWithJellyEnabledTest(const CalendarViewWithJellyEnabledTest&) =
|
||||
delete;
|
||||
CalendarViewWithJellyEnabledTest& operator=(
|
||||
const CalendarViewWithJellyEnabledTest&) = delete;
|
||||
~CalendarViewWithJellyEnabledTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
|
||||
scoped_feature_list_->InitWithFeatures(
|
||||
{features::kCalendarView, features::kCalendarJelly}, {});
|
||||
CalendarViewTest::SetUp();
|
||||
}
|
||||
|
||||
// Assumes current time is "18 Nov 2021 10:00 GMT".
|
||||
std::unique_ptr<google_apis::calendar::EventList>
|
||||
CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway() {
|
||||
auto event_list = std::make_unique<google_apis::calendar::EventList>();
|
||||
event_list->set_time_zone("Greenwich Mean Time");
|
||||
event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
|
||||
"id_0", "summary_0", "18 Nov 2021 12:30 GMT", "18 Nov 2021 13:30 GMT"));
|
||||
|
||||
return event_list;
|
||||
}
|
||||
|
||||
// Assumes current time is "18 Nov 2021 10:00 GMT".
|
||||
std::unique_ptr<google_apis::calendar::EventList>
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway() {
|
||||
auto event_list = std::make_unique<google_apis::calendar::EventList>();
|
||||
event_list->set_time_zone("Greenwich Mean Time");
|
||||
event_list->InjectItemForTesting(calendar_test_utils::CreateEvent(
|
||||
"id_0", "summary_0", "18 Nov 2021 10:10 GMT", "18 Nov 2021 13:30 GMT"));
|
||||
|
||||
return event_list;
|
||||
}
|
||||
|
||||
void MockEventsFetched(
|
||||
base::Time date,
|
||||
std::unique_ptr<google_apis::calendar::EventList> event_list) {
|
||||
Shell::Get()->system_tray_model()->calendar_model()->OnEventsFetched(
|
||||
calendar_utils::GetStartOfMonthUTC(date),
|
||||
google_apis::ApiErrorCode::HTTP_SUCCESS, event_list.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
|
||||
};
|
||||
|
||||
TEST_F(CalendarViewWithJellyEnabledTest,
|
||||
GivenNoEvents_WhenCalendarViewOpens_ThenUpNextViewShouldNotBeShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("7 Jun 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
|
||||
ash::prefs::kCalendarIntegrationEnabled, false);
|
||||
CreateCalendarView();
|
||||
|
||||
// When we've just created the calendar view and not fetched any events, then
|
||||
// up next shouldn't have been created.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
EXPECT_FALSE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CalendarViewWithJellyEnabledTest,
|
||||
GivenEventsStartingMoreThanTwoHoursAway_WhenCalendarViewOpens_ThenUpNextViewShouldNotBeShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
MockEventsFetched(
|
||||
calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());
|
||||
|
||||
// When fetched events are more than two hours away, then up next shouldn't
|
||||
// have been created.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
EXPECT_FALSE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CalendarViewWithJellyEnabledTest,
|
||||
GivenEventsStartingTenMinsAway_WhenCalendarViewOpens_ThenUpNextViewShouldBeShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When fetched events are in the next 10 mins, then up next should have been
|
||||
// created.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
EXPECT_TRUE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CalendarViewWithJellyEnabledTest,
|
||||
GivenUpNextIsShown_WhenNewEventsMoreThanTwoHoursAwayAreFetched_ThenUpNextViewShouldNotBeShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When fetched events are in the next 10 mins, then up next should have been
|
||||
// created.
|
||||
EXPECT_TRUE(up_next_view());
|
||||
|
||||
MockEventsFetched(
|
||||
calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeMoreThanTwoHoursAway());
|
||||
|
||||
// When fetched events are now more than two hours away, then up next
|
||||
// should have been destroyed.
|
||||
EXPECT_FALSE(up_next_view());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
Reference in New Issue
Block a user