~ps-jenkins/indicator-datetime/latestsnapshot-13.10.0+13.10.20131023.2-0ubuntu1

« back to all changes in this revision

Viewing changes to src/service.c

  • Committer: Tarmac
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2013-10-22 22:35:31 UTC
  • mfrom: (278.1.1 indicator-datetime)
  • Revision ID: tarmac-20131022223531-rn7zr7smbnlki2ak
Revert revision 277 which appears to be more feature than bugfix applicable for SRU.

Approved by PS Jenkins bot, Ted Gould.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
 
25
25
#include <glib/gi18n.h>
26
26
#include <gio/gio.h>
27
 
#include <libnotify/notify.h>
28
27
#include <json-glib/json-glib.h>
29
28
#include <url-dispatcher.h>
30
29
 
31
30
#include "dbus-shared.h"
 
31
#include "planner-eds.h"
32
32
#include "timezone-file.h"
33
33
#include "timezone-geoclue.h"
34
34
#include "service.h"
53
53
 
54
54
enum
55
55
{
56
 
  PROP_0,
57
 
  PROP_PLANNER,
58
 
  PROP_LAST
59
 
};
60
 
 
61
 
static GParamSpec * properties[PROP_LAST] = { 0 };
62
 
 
63
 
enum
64
 
{
65
56
  SECTION_HEADER        = (1<<0),
66
57
  SECTION_CALENDAR      = (1<<1),
67
58
  SECTION_APPOINTMENTS  = (1<<2),
128
119
 
129
120
  guint header_timer;
130
121
  guint timezone_timer;
131
 
  guint alarm_timer;
132
122
 
133
123
  /* Which year/month to show in the calendar,
134
124
     and which day should get the cursor.
398
388
  g_date_time_unref (now);
399
389
}
400
390
 
401
 
/***
402
 
****  ALARMS
403
 
***/
404
 
 
405
 
static void set_alarm_timer (IndicatorDatetimeService * self);
406
 
 
407
 
static gboolean
408
 
appointment_has_alarm_url (const struct IndicatorDatetimeAppt * appt)
409
 
{
410
 
  return (appt->has_alarms) &&
411
 
         (appt->url != NULL) &&
412
 
         (g_str_has_prefix (appt->url, "alarm:///"));
413
 
}
414
 
 
415
 
static gboolean
416
 
datetimes_have_the_same_minute (GDateTime * a G_GNUC_UNUSED, GDateTime * b G_GNUC_UNUSED)
417
 
{
418
 
  int ay, am, ad;
419
 
  int by, bm, bd;
420
 
 
421
 
  g_date_time_get_ymd (a, &ay, &am, &ad);
422
 
  g_date_time_get_ymd (b, &by, &bm, &bd);
423
 
 
424
 
  return (ay == by) &&
425
 
         (am == bm) &&
426
 
         (ad == bd) &&
427
 
         (g_date_time_get_hour (a) == g_date_time_get_hour (b)) &&
428
 
         (g_date_time_get_minute (a) == g_date_time_get_minute (b));
429
 
}
430
 
 
431
 
static void
432
 
dispatch_alarm_url (const struct IndicatorDatetimeAppt * appt)
433
 
{
434
 
  gchar * str;
435
 
 
436
 
  g_return_if_fail (appt != NULL);
437
 
  g_return_if_fail (appointment_has_alarm_url (appt));
438
 
 
439
 
  str = g_date_time_format (appt->begin, "%F %T");
440
 
  g_debug ("dispatching url \"%s\" for appointment \"%s\", which begins at %s",
441
 
           appt->url, appt->summary, str);
442
 
  g_free (str);
443
 
 
444
 
  url_dispatch_send (appt->url, NULL, NULL);
445
 
}
446
 
 
447
 
static void
448
 
on_snap_decided (NotifyNotification * notification  G_GNUC_UNUSED,
449
 
                 char               * action,
450
 
                 gpointer             gurl)
451
 
{
452
 
  g_debug ("%s: %s", G_STRFUNC, action);
453
 
 
454
 
  if (!g_strcmp0 (action, "show"))
455
 
    {
456
 
      const gchar * url = gurl;
457
 
      g_debug ("dispatching url '%s'", url);
458
 
      url_dispatch_send (url, NULL, NULL);
459
 
    }
460
 
}
461
 
 
462
 
static void
463
 
show_snap_decision_for_alarm (const struct IndicatorDatetimeAppt * appt)
464
 
{
465
 
  gchar * title;
466
 
  const gchar * body;
467
 
  const gchar * icon_name;
468
 
  NotifyNotification * nn;
469
 
  GError * error;
470
 
 
471
 
  title = g_date_time_format (appt->begin,
472
 
                              get_terse_time_format_string (appt->begin));
473
 
  body = appt->summary;
474
 
  icon_name = ALARM_CLOCK_ICON_NAME;
475
 
  g_debug ("creating a snap decision with title '%s', body '%s', icon '%s'",
476
 
           title, body, icon_name);
477
 
 
478
 
  nn = notify_notification_new (title, body, icon_name);
479
 
  notify_notification_set_hint_string (nn,
480
 
                                       "x-canonical-snap-decisions",
481
 
                                       "true");
482
 
  notify_notification_set_hint_string (nn,
483
 
                                       "x-canonical-private-button-tint",
484
 
                                       "true");
485
 
  notify_notification_add_action (nn, "show", _("Show"),
486
 
                                  on_snap_decided, g_strdup(appt->url), g_free);
487
 
  notify_notification_add_action (nn, "dismiss", _("Dismiss"),
488
 
                                  on_snap_decided, NULL, NULL);
489
 
 
490
 
  error = NULL;
491
 
  notify_notification_show (nn, &error);
492
 
  if (error != NULL)
493
 
    {
494
 
      g_warning ("Unable to show alarm '%s' popup: %s", body, error->message);
495
 
      g_error_free (error);
496
 
      dispatch_alarm_url (appt);
497
 
    }
498
 
 
499
 
  g_free (title);
500
 
}
501
 
 
502
 
static void update_appointment_lists (IndicatorDatetimeService * self);
503
 
 
504
 
static gboolean
505
 
on_alarm_timer (gpointer gself)
506
 
{
507
 
  IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
508
 
  GDateTime * now;
509
 
  GSList * l;
510
 
 
511
 
  /* If there are any alarms at the current time, show a snap decision */
512
 
  now = indicator_datetime_service_get_localtime (self);
513
 
  for (l=self->priv->upcoming_appointments; l!=NULL; l=l->next)
514
 
    {
515
 
      const struct IndicatorDatetimeAppt * appt = l->data;
516
 
 
517
 
      if (appointment_has_alarm_url (appt))
518
 
        if (datetimes_have_the_same_minute (now, appt->begin))
519
 
          show_snap_decision_for_alarm (appt);
520
 
    }
521
 
  g_date_time_unref (now);
522
 
 
523
 
  /* rebuild the alarm list asynchronously.
524
 
     set_upcoming_appointments() will update the alarm timer when this
525
 
     async call is done, so no need to restart the timer here... */
526
 
  update_appointment_lists (self);
527
 
 
528
 
  return G_SOURCE_REMOVE;
529
 
}
530
 
 
531
 
/* if there are upcoming alarms, set the alarm timer to the nearest one.
532
 
   otherwise, unset the alarm timer. */
533
 
static void
534
 
set_alarm_timer (IndicatorDatetimeService * self)
535
 
{
536
 
  priv_t * p;
537
 
  GDateTime * now;
538
 
  GDateTime * alarm_time;
539
 
  GSList * l;
540
 
 
541
 
  p = self->priv;
542
 
  indicator_clear_timer (&p->alarm_timer);
543
 
 
544
 
  now = indicator_datetime_service_get_localtime (self);
545
 
 
546
 
  /* find the time of the next alarm on our calendar */
547
 
  alarm_time = NULL;
548
 
  for (l=p->upcoming_appointments; l!=NULL; l=l->next)
549
 
    {
550
 
      const struct IndicatorDatetimeAppt * appt = l->data;
551
 
 
552
 
      if (appointment_has_alarm_url (appt))
553
 
        if (g_date_time_compare (appt->begin, now) > 0)
554
 
          if (!alarm_time || g_date_time_compare (alarm_time, appt->begin) > 0)
555
 
            alarm_time = appt->begin;
556
 
    }
557
 
 
558
 
  /* if there's an upcoming alarm, set a timer to wake up at that time */
559
 
  if (alarm_time != NULL)
560
 
    {
561
 
      GTimeSpan interval_msec;
562
 
      gchar * str;
563
 
      GDateTime * then;
564
 
 
565
 
      interval_msec = g_date_time_difference (alarm_time, now);
566
 
      interval_msec += G_USEC_PER_SEC; /* fire a moment after alarm_time */
567
 
      interval_msec /= 1000; /* convert from usec to msec */
568
 
 
569
 
      str = g_date_time_format (alarm_time, "%F %T");
570
 
      g_debug ("%s is the next alarm time", str);
571
 
      g_free (str);
572
 
      then = g_date_time_add_seconds (now, interval_msec/1000);
573
 
      str = g_date_time_format (then, "%F %T");
574
 
      g_debug ("%s is when we'll wake up for it", str);
575
 
      g_free (str);
576
 
      g_date_time_unref (then);
577
 
 
578
 
      p->alarm_timer = g_timeout_add_full (G_PRIORITY_HIGH,
579
 
                                           (guint) interval_msec,
580
 
                                           on_alarm_timer,
581
 
                                           self,
582
 
                                           NULL);
583
 
    }
584
 
 
585
 
  g_date_time_unref (now);
586
 
}
587
 
 
588
 
/***
589
 
****
590
 
***/
591
 
 
592
391
static void
593
392
update_internal_timezone (IndicatorDatetimeService * self)
594
393
{
944
743
}
945
744
 
946
745
static void
947
 
add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean phone)
 
746
add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse)
948
747
{
949
748
  const int MAX_APPTS = 5;
950
 
  GDateTime * now;
951
 
  GHashTable * added;
 
749
  GDateTime * now = indicator_datetime_service_get_localtime (self);
952
750
  GSList * appts;
953
751
  GSList * l;
954
752
  int i;
955
753
 
956
 
  now = indicator_datetime_service_get_localtime (self);
957
 
 
958
 
  added = g_hash_table_new (g_str_hash, g_str_equal);
959
 
 
960
754
  /* build appointment menuitems */
961
755
  appts = self->priv->upcoming_appointments;
962
756
  for (l=appts, i=0; l!=NULL && i<MAX_APPTS; l=l->next, i++)
963
757
    {
964
758
      struct IndicatorDatetimeAppt * appt = l->data;
965
 
      char * fmt;
966
 
      gint64 unix_time;
 
759
      char * fmt = get_appointment_time_format (appt, now, self->priv->settings, terse);
 
760
      const gint64 unix_time = g_date_time_to_unix (appt->begin);
967
761
      GMenuItem * menu_item;
968
762
 
969
 
      if (g_hash_table_contains (added, appt->uid))
970
 
        continue;
971
 
 
972
 
      g_hash_table_add (added, appt->uid);
973
 
 
974
 
      fmt = get_appointment_time_format (appt, now, self->priv->settings, phone);
975
 
      unix_time = g_date_time_to_unix (appt->begin);
976
 
 
977
763
      menu_item = g_menu_item_new (appt->summary, NULL);
978
764
 
979
765
      if (appt->has_alarms)
990
776
      g_menu_item_set_attribute (menu_item, "x-canonical-type",
991
777
                                     "s", appt->has_alarms ? "com.canonical.indicator.alarm"
992
778
                                                           : "com.canonical.indicator.appointment");
993
 
 
994
 
      if (phone)
995
 
        g_menu_item_set_action_and_target_value (menu_item,
996
 
                                                 "indicator.activate-appointment",
997
 
                                                 g_variant_new_string (appt->uid));
998
 
      else
999
 
        g_menu_item_set_action_and_target_value (menu_item,
1000
 
                                                 "indicator.activate-planner",
1001
 
                                                 g_variant_new_int64 (unix_time));
 
779
      g_menu_item_set_action_and_target_value (menu_item,
 
780
                                                   "indicator.activate-planner",
 
781
                                                   g_variant_new_int64 (unix_time));
1002
782
      g_menu_append_item (menu, menu_item);
1003
783
      g_object_unref (menu_item);
1004
784
      g_free (fmt);
1005
785
    }
1006
786
 
1007
787
  /* cleanup */
1008
 
  g_hash_table_unref (added);
1009
788
  g_date_time_unref (now);
1010
789
}
1011
790
 
1584
1363
}
1585
1364
 
1586
1365
static void
1587
 
on_activate_appointment (GSimpleAction * a G_GNUC_UNUSED,
1588
 
                         GVariant      * param,
1589
 
                         gpointer        gself)
1590
 
{
1591
 
  priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv;
1592
 
  const gchar * uid = g_variant_get_string (param, NULL);
1593
 
 
1594
 
  if (uid != NULL)
1595
 
    {
1596
 
      const struct IndicatorDatetimeAppt * appt;
1597
 
      GSList * l;
1598
 
 
1599
 
      /* find the appointment that matches that uid */
1600
 
      for (l=p->upcoming_appointments, appt=NULL; l && !appt; l=l->next)
1601
 
        {
1602
 
          const struct IndicatorDatetimeAppt * tmp = l->data;
1603
 
          if (!g_strcmp0 (uid, tmp->uid))
1604
 
            appt = tmp;
1605
 
        }
1606
 
 
1607
 
      /* if that appointment's an alarm, dispatch its url */
1608
 
      g_debug ("%s: uri '%s'; matching appt is %p", G_STRFUNC, uid, appt);
1609
 
      if (appt && appointment_has_alarm_url (appt))
1610
 
        dispatch_alarm_url (appt);
1611
 
    }
1612
 
}
1613
 
 
1614
 
static void
1615
1366
on_phone_clock_activated (GSimpleAction * a      G_GNUC_UNUSED,
1616
1367
                          GVariant      * param  G_GNUC_UNUSED,
1617
1368
                          gpointer        gself  G_GNUC_UNUSED)
1675
1426
    { "activate-phone-settings", on_phone_settings_activated },
1676
1427
    { "activate-phone-clock-app", on_phone_clock_activated },
1677
1428
    { "activate-planner", on_activate_planner, "x", NULL },
1678
 
    { "activate-appointment", on_activate_appointment, "s", NULL },
1679
1429
    { "set-location", on_set_location, "s" }
1680
1430
  };
1681
1431
 
1906
1656
 
1907
1657
  /* sync the menus/actions */
1908
1658
  rebuild_appointments_section_soon (self);
1909
 
 
1910
 
  /* alarm timer is keyed off of the next alarm time,
1911
 
     so it needs to be rebuilt when the appointment list changes */
1912
 
  set_alarm_timer (self);
1913
1659
}
1914
1660
 
1915
1661
static void
2079
1825
***/
2080
1826
 
2081
1827
static void
2082
 
my_get_property (GObject     * o,
2083
 
                 guint         property_id,
2084
 
                 GValue      * value,
2085
 
                 GParamSpec  * pspec)
2086
 
{
2087
 
  IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o);
2088
 
 
2089
 
  switch (property_id)
2090
 
    {
2091
 
      case PROP_PLANNER:
2092
 
        g_value_set_object (value, self->priv->planner);
2093
 
        break;
2094
 
 
2095
 
      default:
2096
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
2097
 
    }
2098
 
}
2099
 
 
2100
 
static void
2101
 
my_set_property (GObject       * o,
2102
 
                 guint           property_id,
2103
 
                 const GValue  * value,
2104
 
                 GParamSpec    * pspec)
2105
 
{
2106
 
  IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o);
2107
 
 
2108
 
  switch (property_id)
2109
 
    {
2110
 
      case PROP_PLANNER:
2111
 
        indicator_datetime_service_set_planner (self, g_value_get_object (value));
2112
 
        break;
2113
 
 
2114
 
      default:
2115
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
2116
 
    }
2117
 
}
2118
 
 
2119
 
 
2120
 
static void
2121
1828
my_dispose (GObject * o)
2122
1829
{
2123
1830
  int i;
2140
1847
 
2141
1848
  set_detect_location_enabled (self, FALSE);
2142
1849
 
2143
 
  indicator_datetime_service_set_planner (self, NULL);
 
1850
  if (p->planner != NULL)
 
1851
    {
 
1852
      g_signal_handlers_disconnect_by_data (p->planner, self);
 
1853
      g_clear_object (&p->planner);
 
1854
    }
 
1855
  g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments);
 
1856
  g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments);
2144
1857
 
2145
1858
  if (p->login1_manager != NULL)
2146
1859
    {
2152
1865
  indicator_clear_timer (&p->rebuild_id);
2153
1866
  indicator_clear_timer (&p->timezone_timer);
2154
1867
  indicator_clear_timer (&p->header_timer);
2155
 
  indicator_clear_timer (&p->alarm_timer);
2156
1868
 
2157
1869
  if (p->settings != NULL)
2158
1870
    {
2243
1955
  p->cancellable = g_cancellable_new ();
2244
1956
 
2245
1957
  /***
 
1958
  ****  Create the planner and listen for changes
 
1959
  ***/
 
1960
 
 
1961
  p->planner = indicator_datetime_planner_eds_new ();
 
1962
 
 
1963
  g_signal_connect_swapped (p->planner, "appointments-changed",
 
1964
                            G_CALLBACK(update_appointment_lists), self);
 
1965
 
 
1966
 
 
1967
  /***
2246
1968
  ****  Create the settings object and listen for changes
2247
1969
  ***/
2248
1970
 
2311
2033
 
2312
2034
  on_local_time_jumped (self);
2313
2035
 
2314
 
  set_alarm_timer (self);
2315
 
 
2316
2036
  for (i=0; i<N_PROFILES; ++i)
2317
2037
    create_menu (self, i);
2318
2038
 
2323
2043
indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
2324
2044
{
2325
2045
  GObjectClass * object_class = G_OBJECT_CLASS (klass);
2326
 
  const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS;
2327
2046
 
2328
2047
  object_class->dispose = my_dispose;
2329
2048
  object_class->finalize = my_finalize;
2330
 
  object_class->get_property = my_get_property;
2331
 
  object_class->set_property = my_set_property;
2332
2049
 
2333
2050
  g_type_class_add_private (klass, sizeof (IndicatorDatetimeServicePrivate));
2334
2051
 
2340
2057
    NULL, NULL,
2341
2058
    g_cclosure_marshal_VOID__VOID,
2342
2059
    G_TYPE_NONE, 0);
2343
 
 
2344
 
  /* install properties */
2345
 
 
2346
 
  properties[PROP_0] = NULL;
2347
 
 
2348
 
  properties[PROP_PLANNER] = g_param_spec_object ("planner",
2349
 
                                                  "Planner",
2350
 
                                                  "The appointment provider",
2351
 
                                                  INDICATOR_TYPE_DATETIME_PLANNER,
2352
 
                                                  flags);
2353
 
 
2354
 
  g_object_class_install_properties (object_class, PROP_LAST, properties);
2355
2060
}
2356
2061
 
2357
2062
/***
2359
2064
***/
2360
2065
 
2361
2066
IndicatorDatetimeService *
2362
 
indicator_datetime_service_new (IndicatorDatetimePlanner * planner)
 
2067
indicator_datetime_service_new (void)
2363
2068
{
2364
 
  GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE,
2365
 
                              "planner", planner,
2366
 
                              NULL);
 
2069
  GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, NULL);
2367
2070
 
2368
2071
  return INDICATOR_DATETIME_SERVICE (o);
2369
2072
}
2399
2102
  if (dirty)
2400
2103
    update_appointment_lists (self);
2401
2104
}
2402
 
 
2403
 
void
2404
 
indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
2405
 
                                        IndicatorDatetimePlanner * planner)
2406
 
{
2407
 
  priv_t * p;
2408
 
 
2409
 
  g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
2410
 
  g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (planner));
2411
 
 
2412
 
  p = self->priv;
2413
 
 
2414
 
  /* clear the old planner & appointments */
2415
 
 
2416
 
  if (p->planner != NULL)
2417
 
    {
2418
 
      g_signal_handlers_disconnect_by_data (p->planner, self);
2419
 
      g_clear_object (&p->planner);
2420
 
    }
2421
 
 
2422
 
  g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments);
2423
 
  g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments);
2424
 
 
2425
 
  /* set the new planner & begin fetching appointments from it */
2426
 
 
2427
 
  if (planner != NULL)
2428
 
    {
2429
 
      p->planner = g_object_ref (planner);
2430
 
 
2431
 
      g_signal_connect_swapped (p->planner, "appointments-changed",
2432
 
                                G_CALLBACK(update_appointment_lists), self);
2433
 
 
2434
 
      update_appointment_lists (self);
2435
 
    }
2436
 
}