Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright 2025 GNOME Foundation, Inc.
4 : : *
5 : : * SPDX-License-Identifier: GPL-2.0-or-later
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Authors:
21 : : * - Ignacy Kuchciński <ignacykuchcinski@gnome.org>
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include <glib/gi18n-lib.h>
27 : : #include <libmalcontent/manager.h>
28 : :
29 : : #include "time-page.h"
30 : : #include "cc-duration-row.h"
31 : : #include "screen-time-statistics-row.h"
32 : : #include "cc-time-row.h"
33 : :
34 : : static gboolean update_session_limits_cb (gpointer data);
35 : :
36 : : /**
37 : : * MctTimePage:
38 : : *
39 : : * A widget which shows parental controls for screen time limits
40 : : * for the selected user.
41 : : *
42 : : * [property@Malcontent.TimePage:user] may be `NULL`, in which case the
43 : : * contents of the widget are undefined and it should not be shown.
44 : : *
45 : : * Since: 0.14.0
46 : : */
47 : : struct _MctTimePage
48 : : {
49 : : AdwNavigationPage parent;
50 : :
51 : : AdwWindowTitle *time_window_title;
52 : : AdwPreferencesGroup *preferences_group;
53 : : AdwPreferencesPage *preferences_page;
54 : : AdwSwitchRow *screen_time_limit_row;
55 : : AdwSwitchRow *bedtime_schedule_row;
56 : : CcDurationRow *daily_time_limit_row;
57 : : CcTimeRow *bedtime_row;
58 : :
59 : : unsigned int update_session_limits_source_id;
60 : : unsigned long session_limits_changed_id;
61 : : unsigned long user_notify_id;
62 : : gboolean flushed_on_dispose;
63 : : GCancellable *cancellable; /* (owned) */
64 : : MctSessionLimits *limits; /* (owned) */
65 : : MctSessionLimits *last_saved_limits; /* (owned); updated each time we internally time out and save the session limits */
66 : : MctScreenTimeStatisticsRow *row; /* (nullable) */
67 : :
68 : : MctUser *user; /* (owned) (nullable) */
69 : : GDBusConnection *connection; /* (owned) */
70 : : MctManager *policy_manager; /* (owned) */
71 : : };
72 : :
73 [ # # # # : 0 : G_DEFINE_TYPE (MctTimePage, mct_time_page, ADW_TYPE_NAVIGATION_PAGE)
# # ]
74 : :
75 : : typedef enum
76 : : {
77 : : PROP_USER = 1,
78 : : PROP_CONNECTION,
79 : : PROP_POLICY_MANAGER,
80 : : } MctTimePageProperty;
81 : :
82 : : static GParamSpec *properties[PROP_POLICY_MANAGER + 1];
83 : :
84 : : static void
85 : 0 : schedule_update_session_limits (MctTimePage *self)
86 : : {
87 [ # # ]: 0 : if (self->update_session_limits_source_id > 0)
88 : 0 : return;
89 : :
90 : : /* Use a timeout to batch multiple quick changes into a single
91 : : * update. 1 second is an arbitrary sufficiently small number */
92 : 0 : self->update_session_limits_source_id =
93 : 0 : g_timeout_add_seconds (1, update_session_limits_cb, self);
94 : : }
95 : :
96 : : static void
97 : 0 : flush_update_session_limits (MctTimePage *self)
98 : : {
99 [ # # ]: 0 : if (self->update_session_limits_source_id > 0)
100 : : {
101 : : /* Remove the timer and forcefully call the timer callback. */
102 : 0 : g_source_remove (self->update_session_limits_source_id);
103 : 0 : self->update_session_limits_source_id = 0;
104 : :
105 : 0 : update_session_limits_cb (self);
106 : : }
107 : 0 : }
108 : :
109 : : static void
110 : 0 : setup_time_page (MctTimePage *self)
111 : : {
112 : : gboolean daily_limit, daily_schedule;
113 : : unsigned int limit_secs, bedtime_secs;
114 : :
115 : 0 : daily_limit = mct_session_limits_get_daily_limit (self->limits, &limit_secs);
116 : : /* By default recommend a 1 hour daily screen time limit.
117 : : * See https://www.familyeducation.com/entertainment-activities/online/screen-time-recommendations-by-age-chart. */
118 [ # # ]: 0 : if (limit_secs == 0)
119 : : {
120 : 0 : limit_secs = 60 * 60;
121 : : }
122 : :
123 : 0 : daily_schedule = mct_session_limits_get_daily_schedule (self->limits,
124 : : NULL,
125 : : &bedtime_secs);
126 [ # # ]: 0 : if (bedtime_secs == 0)
127 : : {
128 : : /* Set a default bedtime of 20:00 */
129 : 0 : bedtime_secs = 20 * 60 * 60;
130 : : }
131 : :
132 : 0 : adw_switch_row_set_active (self->screen_time_limit_row, daily_limit);
133 : 0 : adw_switch_row_set_active (self->bedtime_schedule_row, daily_schedule);
134 : 0 : cc_duration_row_set_duration (self->daily_time_limit_row, limit_secs / 60);
135 : 0 : cc_time_row_set_time (self->bedtime_row, bedtime_secs / 60);
136 : 0 : }
137 : :
138 : : static void
139 : 0 : get_session_limits_cb (GObject *obj,
140 : : GAsyncResult *result,
141 : : gpointer user_data)
142 : : {
143 : 0 : MctManager *policy_manager = MCT_MANAGER (obj);
144 : : MctTimePage *self;
145 [ # # ]: 0 : g_autoptr(MctSessionLimits) new_session_limits = NULL;
146 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
147 : :
148 : : /* This may be called after MctTimePage has been finalised, in an idle
149 : : * callback from cancelling the async call. So we can’t deref `user_data`
150 : : * until we’ve checked for cancellation. */
151 : :
152 : 0 : new_session_limits = mct_manager_get_session_limits_finish (policy_manager,
153 : : result,
154 : : &local_error);
155 [ # # ]: 0 : if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
156 : 0 : return;
157 : :
158 : 0 : self = MCT_TIME_PAGE (user_data);
159 : :
160 [ # # ]: 0 : g_clear_pointer (&self->limits, mct_session_limits_unref);
161 [ # # ]: 0 : g_clear_pointer (&self->last_saved_limits, mct_session_limits_unref);
162 : 0 : self->limits = g_steal_pointer (&new_session_limits);
163 : :
164 [ # # ]: 0 : if (local_error != NULL)
165 : : {
166 : 0 : g_warning ("Error retrieving session limits for user '%s': %s",
167 : : mct_user_get_username (self->user),
168 : : local_error->message);
169 : 0 : return;
170 : : }
171 : :
172 : 0 : self->last_saved_limits = mct_session_limits_ref (self->limits);
173 : :
174 : 0 : g_debug ("Retrieved new session limits for user '%s'",
175 : : mct_user_get_username (self->user));
176 : :
177 : 0 : setup_time_page (self);
178 : : }
179 : :
180 : : static void
181 : 0 : set_session_limits_cb (GObject *obj,
182 : : GAsyncResult *result,
183 : : gpointer user_data)
184 : : {
185 : 0 : MctManager *policy_manager = MCT_MANAGER (obj);
186 : : MctTimePage *self;
187 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
188 : :
189 : : /* This may be called after MctTimePage has been finalised, in an idle
190 : : * callback from cancelling the async call. So we can’t deref `user_data`
191 : : * until we’ve checked for cancellation. */
192 : :
193 [ # # ]: 0 : if (!mct_manager_set_session_limits_finish (policy_manager,
194 : : result,
195 : : &local_error))
196 : : {
197 [ # # ]: 0 : if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
198 : 0 : return;
199 : :
200 : 0 : self = MCT_TIME_PAGE (user_data);
201 : :
202 : 0 : g_warning ("Error updating session limits: %s", local_error->message);
203 : 0 : setup_time_page (self);
204 : : }
205 : : }
206 : :
207 : : static gboolean
208 : 0 : update_session_limits_cb (gpointer data)
209 : : {
210 : 0 : g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
211 : 0 : g_autoptr(MctSessionLimits) new_limits = NULL;
212 : 0 : g_autoptr(GError) error = NULL;
213 : 0 : MctTimePage *self = data;
214 : : guint limit, daily_schedule_start, daily_schedule_end, bedtime_minutes, bedtime_hours;
215 : : gboolean screen_time_limit, bedtime_schedule;
216 : :
217 : 0 : self->update_session_limits_source_id = 0;
218 : :
219 : 0 : screen_time_limit = adw_switch_row_get_active (self->screen_time_limit_row);
220 : 0 : limit = cc_duration_row_get_duration (self->daily_time_limit_row);
221 : 0 : mct_session_limits_builder_set_daily_limit (&builder, screen_time_limit, limit * 60);
222 : :
223 : 0 : bedtime_schedule = adw_switch_row_get_active (self->bedtime_schedule_row);
224 : :
225 : 0 : bedtime_minutes = cc_time_row_get_time (self->bedtime_row);
226 : 0 : bedtime_hours = bedtime_minutes / 60;
227 : 0 : const unsigned int sleep_time_hours = 6;
228 : 0 : const unsigned int midnight = 24;
229 : :
230 : : /* Calculate the start and the end of the daily schedule in seconds.
231 : : * Ensure at least 6 hours of sleep time for late bedtimes. For early
232 : : * bedtimes, the start of the daily schedule would need to be after
233 : : * the end, but split days aren't supported yet, so set it to zero
234 : : * for now. Also make sure the end of the daily schedule is non zero.*/
235 [ # # ]: 0 : if (bedtime_hours + sleep_time_hours >= midnight)
236 : 0 : daily_schedule_start = (bedtime_hours + sleep_time_hours) % midnight * 60 * 60;
237 : : else
238 : 0 : daily_schedule_start = 0;
239 : :
240 [ # # ]: 0 : if (bedtime_minutes != 0)
241 : 0 : daily_schedule_end = bedtime_minutes * 60;
242 : : else
243 : 0 : daily_schedule_end = 1;
244 : :
245 : 0 : mct_session_limits_builder_set_daily_schedule (&builder,
246 : : bedtime_schedule,
247 : : daily_schedule_start,
248 : : daily_schedule_end);
249 : :
250 : 0 : new_limits = mct_session_limits_builder_end (&builder);
251 : :
252 : : /* Don't bother saving the session limit (which could result in asking the
253 : : * user for admin permission) if it hasn't changed. */
254 [ # # # # ]: 0 : if (self->last_saved_limits != NULL &&
255 : 0 : mct_session_limits_equal (new_limits, self->last_saved_limits))
256 : : {
257 : 0 : g_debug ("Not saving session limits as they haven't changed");
258 : 0 : return G_SOURCE_REMOVE;
259 : : }
260 : :
261 : 0 : mct_manager_set_session_limits_async (self->policy_manager,
262 : : mct_user_get_uid (self->user),
263 : : new_limits,
264 : : MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE,
265 : : self->cancellable,
266 : : set_session_limits_cb,
267 : : self);
268 : :
269 : : /* Update the cached copy */
270 [ # # ]: 0 : g_clear_pointer (&self->last_saved_limits, mct_session_limits_unref);
271 : 0 : self->last_saved_limits = g_steal_pointer (&new_limits);
272 : :
273 : 0 : return G_SOURCE_REMOVE;
274 : : }
275 : :
276 : : static void
277 : 0 : screen_time_limit_row_notify_active_cb (MctTimePage *self)
278 : : {
279 : 0 : schedule_update_session_limits (self);
280 : 0 : }
281 : :
282 : : static void
283 : 0 : bedtime_schedule_row_notify_active_cb (MctTimePage *self)
284 : : {
285 : 0 : schedule_update_session_limits (self);
286 : 0 : }
287 : :
288 : : static void
289 : 0 : bedtime_row_notify_time_cb (CcTimeRow *row,
290 : : GParamSpec *pspec,
291 : : gpointer user_data)
292 : : {
293 : 0 : MctTimePage *self = MCT_TIME_PAGE (user_data);
294 : :
295 : 0 : schedule_update_session_limits (self);
296 : 0 : }
297 : :
298 : : static void
299 : 0 : daily_time_limit_row_notify_duration_cb (CcDurationRow *row,
300 : : GParamSpec *pspec,
301 : : gpointer user_data)
302 : : {
303 : 0 : MctTimePage *self = MCT_TIME_PAGE (user_data);
304 : :
305 : 0 : schedule_update_session_limits (self);
306 : 0 : }
307 : :
308 : : static void
309 : 0 : session_limits_changed_cb (MctManager *policy_manager,
310 : : uid_t uid,
311 : : void *user_data)
312 : : {
313 : 0 : MctTimePage *self = MCT_TIME_PAGE (user_data);
314 : :
315 [ # # ]: 0 : if (uid != mct_user_get_uid (self->user))
316 : 0 : return;
317 : :
318 : 0 : mct_manager_get_session_limits_async (self->policy_manager,
319 : : mct_user_get_uid (self->user),
320 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE,
321 : : self->cancellable,
322 : : get_session_limits_cb,
323 : : self);
324 : : }
325 : :
326 : : static void
327 : 0 : mct_time_page_get_property (GObject *object,
328 : : guint prop_id,
329 : : GValue *value,
330 : : GParamSpec *pspec)
331 : : {
332 : 0 : MctTimePage *self = MCT_TIME_PAGE (object);
333 : :
334 [ # # # # ]: 0 : switch ((MctTimePageProperty) prop_id)
335 : : {
336 : 0 : case PROP_USER:
337 : 0 : g_value_set_object (value, self->user);
338 : 0 : break;
339 : :
340 : 0 : case PROP_CONNECTION:
341 : 0 : g_value_set_object (value, self->connection);
342 : 0 : break;
343 : :
344 : 0 : case PROP_POLICY_MANAGER:
345 : 0 : g_value_set_object (value, self->policy_manager);
346 : 0 : break;
347 : :
348 : 0 : default:
349 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
350 : : }
351 : 0 : }
352 : :
353 : : static void
354 : 0 : mct_time_page_set_property (GObject *object,
355 : : guint prop_id,
356 : : const GValue *value,
357 : : GParamSpec *pspec)
358 : : {
359 : 0 : MctTimePage *self = MCT_TIME_PAGE (object);
360 : :
361 [ # # # # ]: 0 : switch ((MctTimePageProperty) prop_id)
362 : : {
363 : 0 : case PROP_USER:
364 : 0 : mct_time_page_set_user (self, g_value_get_object (value));
365 : 0 : break;
366 : :
367 : 0 : case PROP_CONNECTION:
368 : : /* Construct-only. May not be %NULL. */
369 : 0 : g_assert (self->connection == NULL);
370 : 0 : self->connection = g_value_dup_object (value);
371 : 0 : g_assert (self->connection != NULL);
372 : 0 : break;
373 : :
374 : 0 : case PROP_POLICY_MANAGER:
375 : : /* Construct only. */
376 : 0 : g_assert (self->policy_manager == NULL);
377 : 0 : self->policy_manager = g_value_dup_object (value);
378 : 0 : break;
379 : :
380 : 0 : default:
381 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382 : : }
383 : 0 : }
384 : :
385 : : static void
386 : 0 : mct_time_page_constructed (GObject *object)
387 : : {
388 : 0 : MctTimePage *self = MCT_TIME_PAGE (object);
389 : :
390 : : /* Chain up. */
391 : 0 : G_OBJECT_CLASS (mct_time_page_parent_class)->constructed (object);
392 : :
393 : : /* Check our construct properties. */
394 : 0 : g_assert (self->connection != NULL);
395 : 0 : g_assert (MCT_IS_MANAGER (self->policy_manager));
396 : :
397 : 0 : self->session_limits_changed_id =
398 : 0 : g_signal_connect (self->policy_manager,
399 : : "session-limits-changed",
400 : : G_CALLBACK (session_limits_changed_cb),
401 : : self);
402 : 0 : }
403 : :
404 : : static void
405 : 0 : mct_time_page_dispose (GObject *object)
406 : : {
407 : 0 : MctTimePage *self = MCT_TIME_PAGE (object);
408 : :
409 : : /* Since GTK calls g_object_run_dispose(), dispose() may be called multiple
410 : : * times. We definitely want to save any unsaved changes, but don’t need to
411 : : * do it multiple times, and after the first g_object_run_dispose() call,
412 : : * none of our child widgets are still around to extract data from anyway. */
413 [ # # ]: 0 : if (!self->flushed_on_dispose)
414 : 0 : flush_update_session_limits (self);
415 : 0 : self->flushed_on_dispose = TRUE;
416 : :
417 [ # # ]: 0 : if (self->row != NULL)
418 : : {
419 : 0 : adw_preferences_group_remove (self->preferences_group,
420 : 0 : GTK_WIDGET (self->row));
421 : 0 : self->row = NULL;
422 : : }
423 : :
424 [ # # ]: 0 : g_clear_object (&self->connection);
425 : :
426 : 0 : g_cancellable_cancel (self->cancellable);
427 [ # # ]: 0 : g_clear_object (&self->cancellable);
428 : :
429 [ # # ]: 0 : g_clear_signal_handler (&self->user_notify_id, self->user);
430 [ # # ]: 0 : g_clear_object (&self->user);
431 : :
432 [ # # ]: 0 : g_clear_signal_handler (&self->session_limits_changed_id, self->policy_manager);
433 [ # # ]: 0 : g_clear_object (&self->policy_manager);
434 : :
435 : 0 : G_OBJECT_CLASS (mct_time_page_parent_class)->dispose (object);
436 : 0 : }
437 : :
438 : : static void
439 : 0 : mct_time_page_finalize (GObject *object)
440 : : {
441 : 0 : MctTimePage *self = MCT_TIME_PAGE (object);
442 : :
443 : 0 : g_assert (self->update_session_limits_source_id == 0);
444 : :
445 [ # # ]: 0 : g_clear_pointer (&self->limits, mct_session_limits_unref);
446 [ # # ]: 0 : g_clear_pointer (&self->last_saved_limits, mct_session_limits_unref);
447 : :
448 : : /* Hopefully we don’t have data loss. */
449 : 0 : g_assert (self->flushed_on_dispose);
450 : :
451 : 0 : G_OBJECT_CLASS (mct_time_page_parent_class)->finalize (object);
452 : 0 : }
453 : :
454 : : static void
455 : 0 : mct_time_page_class_init (MctTimePageClass *klass)
456 : : {
457 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
458 : 0 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
459 : :
460 : 0 : object_class->get_property = mct_time_page_get_property;
461 : 0 : object_class->set_property = mct_time_page_set_property;
462 : 0 : object_class->constructed = mct_time_page_constructed;
463 : 0 : object_class->dispose = mct_time_page_dispose;
464 : 0 : object_class->finalize = mct_time_page_finalize;
465 : :
466 : : /**
467 : : * MctTimePage:user: (nullable)
468 : : *
469 : : * The currently selected user account.
470 : : *
471 : : * Since 0.14.0
472 : : */
473 : 0 : properties[PROP_USER] =
474 : 0 : g_param_spec_object ("user",
475 : : "User",
476 : : "The currently selected user account.",
477 : : MCT_TYPE_USER,
478 : : G_PARAM_READWRITE |
479 : : G_PARAM_STATIC_STRINGS |
480 : : G_PARAM_EXPLICIT_NOTIFY);
481 : :
482 : : /**
483 : : * MctTimePage:connection: (not nullable)
484 : : *
485 : : * A connection to the system bus, where malcontent-timerd runs.
486 : : *
487 : : * It’s provided to allow an existing connection to be re-used and for testing
488 : : * purposes.
489 : : *
490 : : * Since 0.14.0
491 : : */
492 : 0 : properties[PROP_CONNECTION] = g_param_spec_object ("connection", NULL, NULL,
493 : : G_TYPE_DBUS_CONNECTION,
494 : : G_PARAM_READWRITE |
495 : : G_PARAM_CONSTRUCT_ONLY |
496 : : G_PARAM_STATIC_STRINGS);
497 : :
498 : : /**
499 : : * MctTimePage:policy-manager:
500 : : *
501 : : * Policy manager to provider users’ parental controls policies.
502 : : *
503 : : * Since 0.14.0
504 : : */
505 : 0 : properties[PROP_POLICY_MANAGER] =
506 : 0 : g_param_spec_object ("policy-manager", NULL, NULL,
507 : : MCT_TYPE_MANAGER,
508 : : G_PARAM_READWRITE |
509 : : G_PARAM_CONSTRUCT_ONLY |
510 : : G_PARAM_STATIC_STRINGS);
511 : :
512 : 0 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
513 : :
514 : 0 : g_type_ensure (CC_TYPE_DURATION_ROW);
515 : 0 : g_type_ensure (CC_TYPE_TIME_ROW);
516 : 0 : g_type_ensure (MCT_TYPE_SCREEN_TIME_STATISTICS_ROW);
517 : :
518 : 0 : gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentControl/ui/time-page.ui");
519 : :
520 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, time_window_title);
521 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, preferences_group);
522 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, preferences_page);
523 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, screen_time_limit_row);
524 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, bedtime_schedule_row);
525 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, daily_time_limit_row);
526 : 0 : gtk_widget_class_bind_template_child (widget_class, MctTimePage, bedtime_row);
527 : :
528 : 0 : gtk_widget_class_bind_template_callback (widget_class, screen_time_limit_row_notify_active_cb);
529 : 0 : gtk_widget_class_bind_template_callback (widget_class, bedtime_schedule_row_notify_active_cb);
530 : 0 : gtk_widget_class_bind_template_callback (widget_class, bedtime_row_notify_time_cb);
531 : 0 : gtk_widget_class_bind_template_callback (widget_class, daily_time_limit_row_notify_duration_cb);
532 : 0 : }
533 : :
534 : : static void
535 : 0 : mct_time_page_init (MctTimePage *self)
536 : : {
537 : 0 : g_autoptr(GtkCssProvider) provider = NULL;
538 : :
539 : 0 : gtk_widget_init_template (GTK_WIDGET (self));
540 : :
541 : 0 : provider = gtk_css_provider_new ();
542 : 0 : gtk_css_provider_load_from_resource (provider, "/org/freedesktop/MalcontentControl/ui/wellbeing.css");
543 : 0 : gtk_style_context_add_provider_for_display (gdk_display_get_default (),
544 : 0 : GTK_STYLE_PROVIDER (provider),
545 : : GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
546 : :
547 : 0 : self->cancellable = g_cancellable_new ();
548 : 0 : }
549 : :
550 : : /**
551 : : * mct_time_page_new:
552 : : * @policy_manager: (transfer none): policy manager for querying user parental
553 : : * controls policies
554 : : * @connection: (transfer none): a D-Bus connection to use
555 : : *
556 : : * Create a new [class@Malcontent.TimePage] widget.
557 : : *
558 : : * Returns: (transfer full): a new time page
559 : : * Since: 0.14.0
560 : : */
561 : : MctTimePage *
562 : 0 : mct_time_page_new (MctManager *policy_manager, GDBusConnection *connection)
563 : : {
564 : 0 : g_return_val_if_fail (MCT_IS_MANAGER (policy_manager), NULL);
565 : 0 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
566 : :
567 : 0 : return g_object_new (MCT_TYPE_TIME_PAGE,
568 : : "policy-manager", policy_manager,
569 : : "connection", connection,
570 : : NULL);
571 : : }
572 : :
573 : : /**
574 : : * mct_time_page_get_user:
575 : : * @self: a time page
576 : : *
577 : : * Get the currently selected user.
578 : : *
579 : : * Returns: (transfer none) (nullable): the currently selected user
580 : : * Since: 0.14.0
581 : : */
582 : : MctUser *
583 : 0 : mct_time_page_get_user (MctTimePage *self)
584 : : {
585 : 0 : g_return_val_if_fail (MCT_IS_TIME_PAGE (self), NULL);
586 : :
587 : 0 : return self->user;
588 : : }
589 : :
590 : : static void
591 : 0 : user_notify_cb (GObject *object,
592 : : GParamSpec *pspec,
593 : : void *user_data)
594 : : {
595 : 0 : MctUser *user = MCT_USER (object);
596 : 0 : MctTimePage *self = MCT_TIME_PAGE (user_data);
597 : :
598 : 0 : g_autofree gchar *help_label = NULL;
599 : 0 : adw_window_title_set_subtitle (self->time_window_title,
600 : : mct_user_get_display_name (user));
601 : :
602 : : /* Translators: Replace the link to commonsensemedia.org with some
603 : : * localised guidance for parents/carers on how to set restrictions on
604 : : * their child/caree in a responsible way which is in keeping with the
605 : : * best practice and culture of the region. If no suitable localised
606 : : * guidance exists, and if the default commonsensemedia.org link is not
607 : : * suitable, please file an issue against malcontent so we can discuss
608 : : * further!
609 : : * https://gitlab.freedesktop.org/pwithnall/malcontent/-/issues/new
610 : : */
611 : 0 : help_label = g_strdup_printf (_("It’s recommended that Screen Time "
612 : : "limits and schedules are set as part of "
613 : : "an ongoing conversation with %s. <a href='https://www.commonsensemedia.org/privacy-and-internet-safety'>"
614 : : "Read guidance</a> on what to consider."),
615 : : mct_user_get_display_name (user));
616 : 0 : adw_preferences_page_set_description (self->preferences_page, help_label);
617 : 0 : }
618 : :
619 : : /**
620 : : * mct_time_page_set_user:
621 : : * @self: a time page
622 : : * @user: (nullable): a user
623 : : *
624 : : * Set the currently selected user.
625 : : *
626 : : * Since: 0.14.0
627 : : */
628 : : void
629 : 0 : mct_time_page_set_user (MctTimePage *self,
630 : : MctUser *user)
631 : : {
632 : 0 : g_return_if_fail (MCT_IS_TIME_PAGE (self));
633 : 0 : g_return_if_fail (user == NULL || MCT_IS_USER (user));
634 : :
635 : 0 : g_autoptr(MctUser) old_user = NULL;
636 : : uid_t uid;
637 : :
638 : : /* If we have pending unsaved changes from the previous user, force them to be
639 : : * saved first. */
640 : 0 : flush_update_session_limits (self);
641 : :
642 [ # # ]: 0 : old_user = (self->user != NULL) ? g_object_ref (self->user) : NULL;
643 : :
644 [ # # ]: 0 : if (g_set_object (&self->user, user))
645 : : {
646 [ # # ]: 0 : if (old_user != NULL)
647 [ # # ]: 0 : g_clear_signal_handler (&self->user_notify_id, old_user);
648 : :
649 [ # # ]: 0 : if (user != NULL)
650 : : {
651 : 0 : self->user_notify_id = g_signal_connect (user,
652 : : "notify",
653 : : G_CALLBACK (user_notify_cb),
654 : : self);
655 : 0 : user_notify_cb (G_OBJECT (user), NULL, self);
656 : :
657 : 0 : mct_manager_get_session_limits_async (self->policy_manager,
658 : : mct_user_get_uid (self->user),
659 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE,
660 : : self->cancellable,
661 : : get_session_limits_cb,
662 : : self);
663 : :
664 [ # # ]: 0 : if (self->row != NULL)
665 : : {
666 : 0 : adw_preferences_group_remove (self->preferences_group,
667 : 0 : GTK_WIDGET (self->row));
668 : 0 : self->row = NULL;
669 : : }
670 : :
671 : 0 : uid = mct_user_get_uid (self->user);
672 : 0 : self->row = mct_screen_time_statistics_row_new (self->connection, uid);
673 : 0 : g_object_bind_property (self->daily_time_limit_row, "duration",
674 : 0 : self->row, "daily-limit",
675 : : G_BINDING_SYNC_CREATE);
676 : :
677 : 0 : adw_preferences_group_add (self->preferences_group,
678 : 0 : GTK_WIDGET (self->row));
679 : : }
680 : :
681 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER]);
682 : : }
683 : : }
|