diff options
-rw-r--r-- | debian/changelog | 8 | ||||
-rw-r--r-- | include/datetime/clock-mock.h | 12 | ||||
-rw-r--r-- | include/datetime/clock.h | 4 | ||||
-rw-r--r-- | src/clock-live.cpp | 31 | ||||
-rw-r--r-- | src/clock.cpp | 82 | ||||
-rw-r--r-- | src/main.cpp | 14 | ||||
-rw-r--r-- | src/planner-range.cpp | 7 | ||||
-rw-r--r-- | src/snap.cpp | 11 | ||||
-rw-r--r-- | tests/manual | 16 | ||||
-rw-r--r-- | tests/test-clock.cpp | 92 |
10 files changed, 207 insertions, 70 deletions
diff --git a/debian/changelog b/debian/changelog index 04fcc14..a27953d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +indicator-datetime (13.10.0+14.10.20140915-0ubuntu1) utopic; urgency=low + + [ Charles Kerr ] + * Update the time strings when a powerd Wakeup signal is detected. + (LP: #1359802) + + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 15 Sep 2014 21:03:00 +0000 + indicator-datetime (13.10.0+14.10.20140908.1-0ubuntu1) utopic; urgency=low [ Charles Kerr ] diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index fb9b52f..0e24377 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -41,15 +41,23 @@ public: DateTime localtime() const { return m_localtime; } - void set_localtime(const DateTime& dt) { + void set_localtime(const DateTime& dt) + { const auto old = m_localtime; - m_localtime = dt; + + set_localtime_quietly(dt); + if (!DateTime::is_same_minute(old, m_localtime)) minute_changed(); if (!DateTime::is_same_day(old, m_localtime)) date_changed(); } + void set_localtime_quietly(const DateTime& dt) + { + m_localtime = dt; + } + private: DateTime m_localtime; }; diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 0445aab..8745d24 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -66,7 +66,7 @@ private: **** ***/ -class Timezones; +class Timezone; /** * \brief A live #Clock that provides the actual system time. @@ -74,7 +74,7 @@ class Timezones; class LiveClock: public Clock { public: - LiveClock (const std::shared_ptr<const Timezones>& zones); + LiveClock (const std::shared_ptr<const Timezone>& zones); virtual ~LiveClock(); virtual DateTime localtime() const; diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 21a18a3..68a8a8b 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -18,7 +18,7 @@ */ #include <datetime/clock.h> -#include <datetime/timezones.h> +#include <datetime/timezone.h> namespace unity { namespace indicator { @@ -59,14 +59,15 @@ class LiveClock::Impl { public: - Impl(LiveClock& owner, const std::shared_ptr<const Timezones>& tzd): + Impl(LiveClock& owner, const std::shared_ptr<const Timezone>& timezone_): m_owner(owner), - m_timezones(tzd) + m_timezone(timezone_) { - if (m_timezones) + if (m_timezone) { - m_timezones->timezone.changed().connect([this](const std::string& z) {setTimezone(z);}); - setTimezone(m_timezones->timezone.get()); + auto setter = [this](const std::string& z){setTimezone(z);}; + m_timezone->timezone.changed().connect(setter); + setter(m_timezone->timezone.get()); } restart_minute_timer(); @@ -76,14 +77,14 @@ public: { clearTimer(m_timer); - g_clear_pointer(&m_timezone, g_time_zone_unref); + g_clear_pointer(&m_gtimezone, g_time_zone_unref); } DateTime localtime() const { - g_assert(m_timezone != nullptr); + g_assert(m_gtimezone != nullptr); - auto gdt = g_date_time_new_now(m_timezone); + auto gdt = g_date_time_new_now(m_gtimezone); DateTime ret(gdt); g_date_time_unref(gdt); return ret; @@ -93,8 +94,8 @@ private: void setTimezone(const std::string& str) { - g_clear_pointer(&m_timezone, g_time_zone_unref); - m_timezone = g_time_zone_new(str.c_str()); + g_clear_pointer(&m_gtimezone, g_time_zone_unref); + m_gtimezone = g_time_zone_new(str.c_str()); m_owner.minute_changed(); } @@ -134,15 +135,15 @@ private: protected: LiveClock& m_owner; - GTimeZone* m_timezone = nullptr; - std::shared_ptr<const Timezones> m_timezones; + GTimeZone* m_gtimezone = nullptr; + std::shared_ptr<const Timezone> m_timezone; DateTime m_prev_datetime; unsigned int m_timer = 0; }; -LiveClock::LiveClock(const std::shared_ptr<const Timezones>& tzd): - p(new Impl(*this, tzd)) +LiveClock::LiveClock(const std::shared_ptr<const Timezone>& timezone_): + p(new Impl(*this, timezone_)) { } diff --git a/src/clock.cpp b/src/clock.cpp index 6831732..a04e074 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -41,34 +41,61 @@ class Clock::Impl public: Impl(Clock& owner): - m_owner(owner) + m_owner(owner), + m_cancellable(g_cancellable_new()) { - auto tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM, - "org.freedesktop.login1", - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_login1_appeared, - on_login1_vanished, - this, nullptr); - m_watched_names.insert(tag); - - tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM, - BUS_POWERD_NAME, - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_powerd_appeared, - on_powerd_vanished, - this, nullptr); - m_watched_names.insert(tag); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_bus_ready, this); } - ~Impl() { + g_cancellable_cancel(m_cancellable); + g_object_unref(m_cancellable); + for(const auto& tag : m_watched_names) g_bus_unwatch_name(tag); } private: + static void on_bus_ready(GObject * /*source_object*/, + GAsyncResult * res, + gpointer gself) + { + GError * error = NULL; + GDBusConnection * bus; + + if ((bus = g_bus_get_finish(res, &error))) + { + auto self = static_cast<Impl*>(gself); + + auto tag = g_bus_watch_name_on_connection(bus, + "org.freedesktop.login1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_login1_appeared, + on_login1_vanished, + gself, nullptr); + self->m_watched_names.insert(tag); + + tag = g_bus_watch_name_on_connection(bus, + BUS_POWERD_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_powerd_appeared, + on_powerd_vanished, + gself, nullptr); + self->m_watched_names.insert(tag); + + g_object_unref(bus); + } + else if (error != nullptr) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("%s Couldn't get system bus: %s", G_STRLOC, error->message); + + g_error_free(error); + } + } + void remember_subscription(const std::string & name, GDBusConnection * bus, guint tag) @@ -142,11 +169,11 @@ private: auto tag = g_dbus_connection_signal_subscribe(bus, name_owner, BUS_POWERD_INTERFACE, - "Wakeup", // signal name + "SysPowerStateChange", BUS_POWERD_PATH, nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, - on_wakeup, + on_sys_power_state_change, gself, // user_data nullptr); // user_data closure @@ -161,15 +188,15 @@ private: static_cast<Impl*>(gself)->m_subscriptions[name].clear(); } - static void on_wakeup(GDBusConnection* /*connection*/, - const gchar* /*sender_name*/, - const gchar* /*object_path*/, - const gchar* /*interface_name*/, - const gchar* /*signal_name*/, - GVariant* /*parameters*/, - gpointer gself) + static void on_sys_power_state_change(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { - g_debug("firing clock.minute_changed() due to powerd.Wakeup"); + g_debug("firing clock.minute_changed() due to state change"); static_cast<Impl*>(gself)->m_owner.minute_changed(); } @@ -178,6 +205,7 @@ private: ***/ Clock& m_owner; + GCancellable * m_cancellable = nullptr; std::set<guint> m_watched_names; std::map<std::string,std::vector<std::shared_ptr<GDBusConnection>>> m_subscriptions; }; diff --git a/src/main.cpp b/src/main.cpp index 54517c9..aa8f829 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,20 +63,20 @@ namespace } std::shared_ptr<State> create_state(const std::shared_ptr<Engine>& engine, - const std::shared_ptr<Timezone>& tz) + const std::shared_ptr<Timezone>& timezone_) { // create the live objects auto live_settings = std::make_shared<LiveSettings>(); auto live_timezones = std::make_shared<LiveTimezones>(live_settings, TIMEZONE_FILE); - auto live_clock = std::make_shared<LiveClock>(live_timezones); + auto live_clock = std::make_shared<LiveClock>(timezone_); // create a full-month planner currently pointing to the current month const auto now = live_clock->localtime(); - auto range_planner = std::make_shared<SimpleRangePlanner>(engine, tz); + auto range_planner = std::make_shared<SimpleRangePlanner>(engine, timezone_); auto calendar_month = std::make_shared<MonthPlanner>(range_planner, now); // create an upcoming-events planner currently pointing to the current date - range_planner = std::make_shared<SimpleRangePlanner>(engine, tz); + range_planner = std::make_shared<SimpleRangePlanner>(engine, timezone_); auto calendar_upcoming = std::make_shared<UpcomingPlanner>(range_planner, now); // create the state @@ -127,8 +127,8 @@ main(int /*argc*/, char** /*argv*/) textdomain(GETTEXT_PACKAGE); auto engine = create_engine(); - auto timezone = std::make_shared<FileTimezone>(TIMEZONE_FILE); - auto state = create_state(engine, timezone); + auto timezone_ = std::make_shared<FileTimezone>(TIMEZONE_FILE); + auto state = create_state(engine, timezone_); auto actions = std::make_shared<LiveActions>(state); MenuFactory factory(actions, state); @@ -136,7 +136,7 @@ main(int /*argc*/, char** /*argv*/) auto snooze_planner = std::make_shared<SnoozePlanner>(state->settings, state->clock); auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service"); std::unique_ptr<Snap> snap (new Snap(notification_engine, state->settings)); - auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone); + auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone_); auto on_snooze = [snooze_planner](const Appointment& a) {snooze_planner->add(a);}; auto on_ok = [](const Appointment&){}; auto on_alarm_reached = [&snap, &on_snooze, &on_ok](const Appointment& a) {(*snap)(a, on_snooze, on_ok);}; diff --git a/src/planner-range.cpp b/src/planner-range.cpp index 41b0f56..c223665 100644 --- a/src/planner-range.cpp +++ b/src/planner-range.cpp @@ -28,7 +28,7 @@ namespace datetime { ***/ SimpleRangePlanner::SimpleRangePlanner(const std::shared_ptr<Engine>& engine, - const std::shared_ptr<Timezone>& timezone): + const std::shared_ptr<Timezone>& timezone): m_engine(engine), m_timezone(timezone), m_range(std::pair<DateTime,DateTime>(DateTime::NowLocal(), DateTime::NowLocal())) @@ -38,6 +38,11 @@ SimpleRangePlanner::SimpleRangePlanner(const std::shared_ptr<Engine>& engine, rebuild_soon(); }); + m_timezone->timezone.changed().connect([this](const std::string& s){ + g_debug("RangePlanner %p rebuilding soon because the timezone changed to '%s'", this, s.c_str()); + rebuild_soon(); + }); + range().changed().connect([this](const std::pair<DateTime,DateTime>&){ g_debug("rebuilding because the date range changed"); rebuild_soon(); diff --git a/src/snap.cpp b/src/snap.cpp index b3368a1..c2cbc0a 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -94,7 +94,16 @@ public: b.add_hint (uin::Builder::HINT_TINT); b.add_hint (uin::Builder::HINT_NONSHAPEDICON); - const auto timefmt = is_locale_12h() ? _("%a, %l:%M %p") : _("%a, %H:%M"); + const char * timefmt; + if (is_locale_12h()) { + /** strftime(3) format for abbreviated weekday, + hours, minutes in a 12h locale; e.g. Wed, 2:00 PM */ + timefmt = _("%a, %l:%M %p"); + } else { + /** A strftime(3) format for abbreviated weekday, + hours, minutes in a 24h locale; e.g. Wed, 14:00 */ + timefmt = _("%a, %H:%M"); + } const auto timestr = appointment.begin.format(timefmt); auto title = g_strdup_printf(_("Alarm %s"), timestr.c_str()); b.set_title (title); diff --git a/tests/manual b/tests/manual index e9f858e..87ad674 100644 --- a/tests/manual +++ b/tests/manual @@ -22,6 +22,13 @@ Test-case indicator-datetime/unity8-items-check <dd>The menu is populated with items</dd> </dl> +Test-case indicator-datetime/timestamp-wakeup +<dl> + <dt>Unplug the phone from any USB connection and put it to sleep</dt> + <dd>Reawaken the device.</dt> + <dd>The indicator should be showing the correct time.</dd> +</dl> + Test-case indicator-datetime/new-alarm-wakeup <dl> <dt>Create and save an upcoming alarm in ubuntu-clock-app</dt> @@ -33,6 +40,15 @@ Test-case indicator-datetime/new-alarm-wakeup <dd>If the device supports haptic feedback, confirm the alarm vibrates.</dd> </dl> +Test-case indicator-datetime/alarm-timezone +<dl> + <dt>In ubuntu-system-settings, change your timezone to a zone you're not in</dt> + <dt>In ubuntu-clock-app, create and save an upcoming alarm</dt> + <dd>The indicator's menu should show the alarm to click at the specified time</dd> + <dt>In ubuntu-system-settings, change back to your correct timezone</dt> + <dd>The indicator's menu should still show the alarm to click at the specified time</dd> +</dl> + Test-case indicator-datetime/snooze <dl> <dt>Create and save an upcoming alarm in ubuntu-clock-app</dt> diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index 82f8cb0..5601d35 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -18,9 +18,13 @@ */ #include <datetime/clock.h> -#include <datetime/timezones.h> +#include <datetime/clock-mock.h> +#include <datetime/timezone.h> + +#include <notifications/dbus-shared.h> #include "test-dbus-fixture.h" +#include "timezone-mock.h" /*** **** @@ -37,9 +41,9 @@ class ClockFixture: public TestDBusFixture TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) { // start up a live clock - std::shared_ptr<Timezones> zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); + auto timezone_ = std::make_shared<MockTimezone>(); + timezone_->timezone.set("America/New_York"); + LiveClock clock(timezone_); wait_msec(500); // wait for the bus to set up // count how many times clock.minute_changed() is emitted over the next minute @@ -62,17 +66,17 @@ TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) TEST_F(ClockFixture, HelloFixture) { - std::shared_ptr<Timezones> zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); + auto timezone_ = std::make_shared<MockTimezone>(); + timezone_->timezone.set("America/New_York"); + LiveClock clock(timezone_); } TEST_F(ClockFixture, TimezoneChangeTriggersSkew) { - std::shared_ptr<Timezones> zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); + auto timezone_ = std::make_shared<MockTimezone>(); + timezone_->timezone.set("America/New_York"); + LiveClock clock(timezone_); auto tz_nyc = g_time_zone_new("America/New_York"); auto now_nyc = g_date_time_new_now(tz_nyc); @@ -87,9 +91,9 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_main_loop_quit(loop); }); g_idle_add([](gpointer gs){ - static_cast<Timezones*>(gs)->timezone.set("America/Los_Angeles"); + static_cast<Timezone*>(gs)->timezone.set("America/Los_Angeles"); return G_SOURCE_REMOVE; - }, zones.get()); + }, timezone_.get()); g_main_loop_run(loop); auto tz_la = g_time_zone_new("America/Los_Angeles"); @@ -127,9 +131,9 @@ namespace */ TEST_F(ClockFixture, SleepTriggersSkew) { - std::shared_ptr<Timezones> zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); + auto timezone_ = std::make_shared<MockTimezone>(); + timezone_->timezone.set("America/New_York"); + LiveClock clock(timezone_); wait_msec(250); // wait for the bus to set up bool skewed = false; @@ -152,3 +156,61 @@ TEST_F(ClockFixture, SleepTriggersSkew) g_bus_unown_name(name_tag); } + +namespace +{ + void on_powerd_name_acquired(GDBusConnection * /*connection*/, + const gchar * /*name*/, + gpointer is_owned) + { + *static_cast<bool*>(is_owned) = true; + } +} + +/** + * Confirm that powerd's SysPowerStateChange triggers + * a timestamp change + */ +TEST_F(ClockFixture, SysPowerStateChange) +{ + // set up the mock clock + bool minute_changed = false; + auto clock = std::make_shared<MockClock>(DateTime::NowLocal()); + clock->minute_changed.connect([&minute_changed]() { + minute_changed = true; + }); + + // control test -- minute_changed shouldn't get triggered + // when the clock is silently changed + gboolean is_owned = false; + auto tag = g_bus_own_name_on_connection(system_bus, + BUS_POWERD_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_powerd_name_acquired, + nullptr, + &is_owned /* user_data */, + nullptr /* user_data closure */); + const DateTime not_now {DateTime::Local(1999, 12, 31, 23, 59, 59)}; + clock->set_localtime_quietly(not_now); + wait_msec(); + ASSERT_TRUE(is_owned); + ASSERT_FALSE(minute_changed); + + // now for the actual test, + // confirm that SysPowerStateChange triggers a minute_changed() signal + GError * error = nullptr; + auto emitted = g_dbus_connection_emit_signal(system_bus, + nullptr, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + "SysPowerStateChange", + g_variant_new("(i)", 1), + &error); + wait_msec(); + EXPECT_TRUE(emitted); + EXPECT_EQ(nullptr, error); + EXPECT_TRUE(minute_changed); + + // cleanup + g_bus_unown_name(tag); +} |