GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-fingerprint-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 658 0.0%
Functions: 0 60 0.0%
Branches: 0 318 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2020 Canonical Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 *
20 * Authors: Marco Trevisan <marco.trevisan@canonical.com>
21 */
22
23 #include <glib/gi18n.h>
24 #include <cairo/cairo.h>
25
26 #include "cc-fingerprint-dialog.h"
27
28 #include "cc-fingerprint-manager.h"
29 #include "cc-fprintd-generated.h"
30 #include "cc-list-row.h"
31
32 #include "config.h"
33
34 #define CC_FPRINTD_NAME "net.reactivated.Fprint"
35
36 /* Translate fprintd strings */
37 #define TR(s) dgettext ("fprintd", s)
38 #include "fingerprint-strings.h"
39
40 typedef enum {
41 DIALOG_STATE_NONE = 0,
42 DIALOG_STATE_DEVICES_LISTING = (1 << 0),
43 DIALOG_STATE_DEVICE_CLAIMING = (1 << 1),
44 DIALOG_STATE_DEVICE_CLAIMED = (1 << 2),
45 DIALOG_STATE_DEVICE_PRINTS_LISTING = (1 << 3),
46 DIALOG_STATE_DEVICE_RELEASING = (1 << 4),
47 DIALOG_STATE_DEVICE_ENROLL_STARTING = (1 << 5),
48 DIALOG_STATE_DEVICE_ENROLLING = (1 << 6),
49 DIALOG_STATE_DEVICE_ENROLL_STOPPING = (1 << 7),
50 DIALOG_STATE_DEVICE_DELETING = (1 << 8),
51
52 DIALOG_STATE_IDLE = DIALOG_STATE_DEVICE_CLAIMED | DIALOG_STATE_DEVICE_ENROLLING,
53 } DialogState;
54
55 struct _CcFingerprintDialog
56 {
57 GtkWindow parent_instance;
58
59 GtkButton *back_button;
60 GtkButton *cancel_button;
61 GtkButton *delete_prints_button;
62 GtkButton *done_button;
63 GtkBox *add_print_popover_box;
64 GtkEntry *enroll_print_entry;
65 GtkFlowBox *prints_gallery;
66 GtkHeaderBar *titlebar;
67 GtkImage *enroll_result_image;
68 GtkLabel *enroll_message;
69 GtkLabel *enroll_result_message;
70 GtkLabel *infobar_error;
71 GtkLabel *title;
72 GtkListBox *devices_list;
73 GtkPopover *add_print_popover;
74 GtkSpinner *spinner;
75 GtkStack *stack;
76 GtkWidget *add_print_icon;
77 GtkWidget *delete_confirmation_infobar;
78 GtkWidget *device_selector;
79 GtkWidget *enroll_print_bin;
80 GtkWidget *enroll_result_icon;
81 GtkWidget *enrollment_view;
82 GtkWidget *error_infobar;
83 GtkWidget *no_devices_found;
84 GtkWidget *prints_manager;
85
86 CcFingerprintManager *manager;
87 DialogState dialog_state;
88 CcFprintdDevice *device;
89 gulong device_signal_id;
90 gulong device_name_owner_id;
91 GCancellable *cancellable;
92 GStrv enrolled_fingers;
93 guint enroll_stages_passed;
94 guint enroll_stage_passed_id;
95 gdouble enroll_progress;
96 };
97
98 /* TODO - fprintd and API changes required:
99 - Identify the finger when the enroll dialog is visible
100 + Only if device supports identification
101 ยท And only in such case support enrolling more than one finger
102 - Delete a single fingerprint | and remove the "Delete all" button
103 - Highlight the finger when the sensor is touched during enrollment
104 - Add customized labels to fingerprints
105 - Devices hotplug (object manager)
106 */
107
108 G_DEFINE_TYPE (CcFingerprintDialog, cc_fingerprint_dialog, GTK_TYPE_WINDOW)
109
110 enum {
111 PROP_0,
112 PROP_MANAGER,
113 N_PROPS
114 };
115
116 #define N_VALID_FINGERS G_N_ELEMENTS (FINGER_IDS) - 1
117 /* The order of the fingers here will affect the UI order */
118 const char * FINGER_IDS[] = {
119 "right-index-finger",
120 "left-index-finger",
121 "right-thumb",
122 "right-middle-finger",
123 "right-ring-finger",
124 "right-little-finger",
125 "left-thumb",
126 "left-middle-finger",
127 "left-ring-finger",
128 "left-little-finger",
129 "any",
130 };
131
132 typedef enum {
133 ENROLL_STATE_NORMAL,
134 ENROLL_STATE_RETRY,
135 ENROLL_STATE_SUCCESS,
136 ENROLL_STATE_WARNING,
137 ENROLL_STATE_ERROR,
138 ENROLL_STATE_COMPLETED,
139 N_ENROLL_STATES,
140 } EnrollState;
141
142 const char * ENROLL_STATE_CLASSES[N_ENROLL_STATES] = {
143 "",
144 "retry",
145 "success",
146 "warning",
147 "error",
148 "completed",
149 };
150
151 static GParamSpec *properties[N_PROPS];
152
153 CcFingerprintDialog *
154 cc_fingerprint_dialog_new (CcFingerprintManager *manager)
155 {
156 return g_object_new (CC_TYPE_FINGERPRINT_DIALOG,
157 "fingerprint-manager", manager,
158 NULL);
159 }
160
161 static gboolean
162 update_dialog_state (CcFingerprintDialog *self,
163 DialogState state)
164 {
165 if (self->dialog_state == state)
166 return FALSE;
167
168 self->dialog_state = state;
169
170 if (self->dialog_state == DIALOG_STATE_NONE ||
171 self->dialog_state == (self->dialog_state & DIALOG_STATE_IDLE))
172 {
173 gtk_spinner_stop (self->spinner);
174 }
175 else
176 {
177 gtk_spinner_start (self->spinner);
178 }
179
180 return TRUE;
181 }
182
183 static gboolean
184 add_dialog_state (CcFingerprintDialog *self,
185 DialogState state)
186 {
187 return update_dialog_state (self, (self->dialog_state | state));
188 }
189
190 static gboolean
191 remove_dialog_state (CcFingerprintDialog *self,
192 DialogState state)
193 {
194 return update_dialog_state (self, (self->dialog_state & ~state));
195 }
196
197 typedef struct
198 {
199 CcFingerprintDialog *dialog;
200 DialogState state;
201 } DialogStateRemover;
202
203 static DialogStateRemover *
204 auto_state_remover (CcFingerprintDialog *self,
205 DialogState state)
206 {
207 DialogStateRemover *state_remover;
208
209 state_remover = g_new0 (DialogStateRemover, 1);
210 state_remover->dialog = g_object_ref (self);
211 state_remover->state = state;
212
213 return state_remover;
214 }
215
216 static void
217 auto_state_remover_cleanup (DialogStateRemover *state_remover)
218 {
219 remove_dialog_state (state_remover->dialog, state_remover->state);
220 g_clear_object (&state_remover->dialog);
221 g_free (state_remover);
222 }
223
224 G_DEFINE_AUTOPTR_CLEANUP_FUNC (DialogStateRemover, auto_state_remover_cleanup);
225
226 static const char *
227 dbus_error_to_human (CcFingerprintDialog *self,
228 GError *error)
229 {
230 g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
231
232 if (dbus_error == NULL)
233 { /* Fallback to generic */ }
234 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.ClaimDevice"))
235 return _("the device needs to be claimed to perform this action");
236 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.AlreadyInUse"))
237 return _("the device is already claimed by another process");
238 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.PermissionDenied"))
239 return _("you do not have permission to perform the action");
240 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.NoEnrolledPrints"))
241 return _("no prints have been enrolled");
242 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.NoActionInProgress"))
243 { /* Fallback to generic */ }
244 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.InvalidFingername"))
245 { /* Fallback to generic */ }
246 else if (g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.Internal"))
247 { /* Fallback to generic */ }
248
249 if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
250 return _("Failed to communicate with the device during enrollment");
251
252 if (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED ||
253 self->dialog_state & DIALOG_STATE_DEVICE_CLAIMING)
254 return _("Failed to communicate with the fingerprint reader");
255
256 return _("Failed to communicate with the fingerprint daemon");
257 }
258
259 static void
260 disconnect_device_signals (CcFingerprintDialog *self)
261 {
262 if (!self->device)
263 return;
264
265 if (self->device_signal_id)
266 {
267 g_signal_handler_disconnect (self->device, self->device_signal_id);
268 self->device_signal_id = 0;
269 }
270
271 if (self->device_name_owner_id)
272 {
273 g_signal_handler_disconnect (self->device, self->device_name_owner_id);
274 self->device_name_owner_id = 0;
275 }
276 }
277
278 static void
279 cc_fingerprint_dialog_get_property (GObject *object,
280 guint prop_id,
281 GValue *value,
282 GParamSpec *pspec)
283 {
284 CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
285
286 switch (prop_id)
287 {
288 case PROP_MANAGER:
289 g_value_set_object (value, self->manager);
290 break;
291
292 default:
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294 }
295 }
296
297 static void
298 cc_fingerprint_dialog_set_property (GObject *object,
299 guint prop_id,
300 const GValue *value,
301 GParamSpec *pspec)
302 {
303 CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
304
305 switch (prop_id)
306 {
307 case PROP_MANAGER:
308 g_set_object (&self->manager, g_value_get_object (value));
309 break;
310
311 default:
312 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313 }
314 }
315
316 static void
317 notify_error (CcFingerprintDialog *self,
318 const char *error_message)
319 {
320 if (error_message)
321 gtk_label_set_label (self->infobar_error, error_message);
322
323 gtk_widget_set_visible (self->error_infobar, error_message != NULL);
324 }
325
326 static GtkWidget *
327 fingerprint_icon_new (const char *icon_name,
328 const char *label_text,
329 GType icon_widget_type,
330 gpointer progress_data,
331 GtkWidget **out_icon,
332 GtkWidget **out_label)
333 {
334 GtkWidget *box;
335 GtkWidget *label;
336 GtkWidget *image;
337 GtkWidget *icon_widget;
338
339 g_return_val_if_fail (g_type_is_a (icon_widget_type, GTK_TYPE_WIDGET), NULL);
340
341 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
342 gtk_widget_set_name (box, "fingerprint-box");
343 gtk_widget_set_hexpand (box, TRUE);
344
345 image = gtk_image_new_from_icon_name (icon_name);
346
347 if (icon_widget_type == GTK_TYPE_IMAGE)
348 icon_widget = image;
349 else
350 icon_widget = g_object_new (icon_widget_type, NULL);
351
352 if (g_type_is_a (icon_widget_type, GTK_TYPE_MENU_BUTTON))
353 {
354 gtk_menu_button_set_child (GTK_MENU_BUTTON (icon_widget), image);
355 gtk_widget_set_can_focus (icon_widget, FALSE);
356 }
357
358 gtk_widget_set_halign (icon_widget, GTK_ALIGN_CENTER);
359 gtk_widget_set_valign (icon_widget, GTK_ALIGN_CENTER);
360 gtk_widget_set_name (icon_widget, "fingerprint-image");
361
362 gtk_box_append (GTK_BOX (box), icon_widget);
363
364 gtk_widget_add_css_class (icon_widget, "circular");
365
366 label = gtk_label_new_with_mnemonic (label_text);
367 gtk_box_append (GTK_BOX (box), label);
368
369 gtk_widget_add_css_class (box, "fingerprint-icon");
370
371 if (out_icon)
372 *out_icon = icon_widget;
373
374 if (out_label)
375 *out_label = label;
376
377 return box;
378 }
379
380 static GtkWidget *
381 fingerprint_menu_button (const char *icon_name,
382 const char *label_text)
383 {
384 GtkWidget *flowbox_child;
385 GtkWidget *button;
386 GtkWidget *label;
387 GtkWidget *box;
388
389 box = fingerprint_icon_new (icon_name, label_text, GTK_TYPE_MENU_BUTTON, NULL,
390 &button, &label);
391
392 flowbox_child = gtk_flow_box_child_new ();
393 gtk_widget_set_focus_on_click (flowbox_child, FALSE);
394 gtk_widget_set_name (flowbox_child, "fingerprint-flowbox");
395
396 gtk_flow_box_child_set_child (GTK_FLOW_BOX_CHILD (flowbox_child), box);
397
398 g_object_set_data (G_OBJECT (flowbox_child), "button", button);
399 g_object_set_data (G_OBJECT (flowbox_child), "icon",
400 GTK_IMAGE (gtk_menu_button_get_child (GTK_MENU_BUTTON (button))));
401 g_object_set_data (G_OBJECT (flowbox_child), "label", label);
402 g_object_set_data (G_OBJECT (button), "flowbox-child", flowbox_child);
403
404 return flowbox_child;
405 }
406
407 static gboolean
408 prints_visibility_filter (GtkFlowBoxChild *child,
409 gpointer user_data)
410 {
411 CcFingerprintDialog *self = user_data;
412 const char *finger_id;
413
414 if (gtk_stack_get_visible_child (self->stack) != self->prints_manager)
415 return FALSE;
416
417 finger_id = g_object_get_data (G_OBJECT (child), "finger-id");
418
419 if (!finger_id)
420 return TRUE;
421
422 if (!self->enrolled_fingers)
423 return FALSE;
424
425 return g_strv_contains ((const gchar **) self->enrolled_fingers, finger_id);
426 }
427
428 static GList *
429 get_container_children (GtkWidget *container)
430 {
431 GtkWidget *child;
432 GList *list = NULL;
433
434 child = gtk_widget_get_first_child (container);
435 while (child) {
436 GtkWidget *next = gtk_widget_get_next_sibling (child);
437
438 list = g_list_append (list, child);
439
440 child = next;
441 }
442
443 return list;
444 }
445
446 static void
447 update_prints_to_add_visibility (CcFingerprintDialog *self)
448 {
449 g_autoptr(GList) print_buttons = NULL;
450 GList *l;
451 guint i;
452
453 print_buttons = get_container_children (GTK_WIDGET (self->add_print_popover_box));
454
455 for (i = 0, l = print_buttons; i < N_VALID_FINGERS && l; ++i, l = l->next)
456 {
457 GtkWidget *button = l->data;
458 gboolean enrolled;
459
460 enrolled = self->enrolled_fingers &&
461 g_strv_contains ((const gchar **) self->enrolled_fingers,
462 FINGER_IDS[i]);
463
464 gtk_widget_set_visible (button, !enrolled);
465 }
466 }
467
468 static void
469 update_prints_visibility (CcFingerprintDialog *self)
470 {
471 update_prints_to_add_visibility (self);
472
473 gtk_flow_box_invalidate_filter (self->prints_gallery);
474 }
475
476 static void
477 list_enrolled_cb (GObject *object,
478 GAsyncResult *res,
479 gpointer user_data)
480 {
481 g_auto(GStrv) enrolled_fingers = NULL;
482 g_autoptr(GError) error = NULL;
483 g_autoptr(DialogStateRemover) state_remover = NULL;
484 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
485 CcFingerprintDialog *self = user_data;
486 guint n_enrolled_fingers = 0;
487
488 cc_fprintd_device_call_list_enrolled_fingers_finish (fprintd_device,
489 &enrolled_fingers,
490 res, &error);
491
492 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
493 return;
494
495 state_remover = auto_state_remover (self, DIALOG_STATE_DEVICE_PRINTS_LISTING);
496
497 gtk_widget_set_sensitive (GTK_WIDGET (self->add_print_icon), TRUE);
498
499 if (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED)
500 gtk_widget_set_sensitive (GTK_WIDGET (self->prints_manager), TRUE);
501
502 if (error)
503 {
504 g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
505
506 if (!dbus_error || !g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.NoEnrolledPrints"))
507 {
508 g_autofree char *error_message = NULL;
509
510 error_message = g_strdup_printf (_("Failed to list fingerprints: %s"),
511 dbus_error_to_human (self, error));
512 g_warning ("Listing of fingerprints on device %s failed: %s",
513 cc_fprintd_device_get_name (self->device), error->message);
514 notify_error (self, error_message);
515 return;
516 }
517 }
518 else
519 {
520 n_enrolled_fingers = g_strv_length (enrolled_fingers);
521 }
522
523 self->enrolled_fingers = g_steal_pointer (&enrolled_fingers);
524 gtk_flow_box_set_max_children_per_line (self->prints_gallery,
525 MIN (3, n_enrolled_fingers + 1));
526
527 update_prints_visibility (self);
528
529 if (n_enrolled_fingers == N_VALID_FINGERS)
530 gtk_widget_set_sensitive (self->add_print_icon, FALSE);
531
532 if (n_enrolled_fingers > 0)
533 gtk_widget_set_visible (GTK_WIDGET (self->delete_prints_button), TRUE);
534 }
535
536 static void
537 update_prints_store (CcFingerprintDialog *self)
538 {
539 ActUser *user;
540
541 g_assert_true (CC_FPRINTD_IS_DEVICE (self->device));
542
543 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_PRINTS_LISTING))
544 return;
545
546 gtk_widget_set_sensitive (GTK_WIDGET (self->add_print_icon), FALSE);
547 gtk_widget_set_visible (GTK_WIDGET (self->delete_prints_button), FALSE);
548
549 g_clear_pointer (&self->enrolled_fingers, g_strfreev);
550
551 user = cc_fingerprint_manager_get_user (self->manager);
552 cc_fprintd_device_call_list_enrolled_fingers (self->device,
553 act_user_get_user_name (user),
554 self->cancellable,
555 list_enrolled_cb,
556 self);
557 }
558
559 static void
560 delete_prints_cb (GObject *object,
561 GAsyncResult *res,
562 gpointer user_data)
563 {
564 g_autoptr(GError) error = NULL;
565 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
566 CcFingerprintDialog *self = user_data;
567
568 cc_fprintd_device_call_delete_enrolled_fingers2_finish (fprintd_device, res, &error);
569
570 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
571 return;
572
573 if (error)
574 {
575 g_autofree char *error_message = NULL;
576
577 error_message = g_strdup_printf (_("Failed to delete saved fingerprints: %s"),
578 dbus_error_to_human (self, error));
579 g_warning ("Deletion of fingerprints on device %s failed: %s",
580 cc_fprintd_device_get_name (self->device), error->message);
581 notify_error (self, error_message);
582 }
583
584 update_prints_store (self);
585 cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
586 }
587
588 static void
589 delete_enrolled_prints (CcFingerprintDialog *self)
590 {
591 g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED);
592
593 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_DELETING))
594 return;
595
596 gtk_widget_set_sensitive (GTK_WIDGET (self->prints_manager), FALSE);
597
598 cc_fprintd_device_call_delete_enrolled_fingers2 (self->device,
599 self->cancellable,
600 delete_prints_cb,
601 self);
602 }
603
604 static const char *
605 get_finger_name (const char *finger_id)
606 {
607 if (g_str_equal (finger_id, "left-thumb"))
608 return _("Left thumb");
609 if (g_str_equal (finger_id, "left-middle-finger"))
610 return _("Left middle finger");
611 if (g_str_equal (finger_id, "left-index-finger"))
612 return _("_Left index finger");
613 if (g_str_equal (finger_id, "left-ring-finger"))
614 return _("Left ring finger");
615 if (g_str_equal (finger_id, "left-little-finger"))
616 return _("Left little finger");
617 if (g_str_equal (finger_id, "right-thumb"))
618 return _("Right thumb");
619 if (g_str_equal (finger_id, "right-middle-finger"))
620 return _("Right middle finger");
621 if (g_str_equal (finger_id, "right-index-finger"))
622 return _("_Right index finger");
623 if (g_str_equal (finger_id, "right-ring-finger"))
624 return _("Right ring finger");
625 if (g_str_equal (finger_id, "right-little-finger"))
626 return _("Right little finger");
627
628 g_return_val_if_reached (_("Unknown Finger"));
629 }
630
631 static gboolean
632 have_multiple_devices (CcFingerprintDialog *self)
633 {
634 g_autoptr(GList) devices_rows = NULL;
635
636 devices_rows = get_container_children (GTK_WIDGET (self->devices_list));
637
638 return devices_rows && devices_rows->next;
639 }
640
641 static void
642 set_enroll_result_message (CcFingerprintDialog *self,
643 EnrollState enroll_state,
644 const char *message)
645 {
646 const char *icon_name;
647 guint i;
648
649 g_return_if_fail (enroll_state >= 0 && enroll_state < N_ENROLL_STATES);
650
651 switch (enroll_state)
652 {
653 case ENROLL_STATE_WARNING:
654 case ENROLL_STATE_ERROR:
655 icon_name = "fingerprint-detection-warning-symbolic";
656 break;
657 case ENROLL_STATE_COMPLETED:
658 icon_name = "fingerprint-detection-complete-symbolic";
659 break;
660 default:
661 icon_name = "fingerprint-detection-symbolic";
662 }
663
664 for (i = 0; i < N_ENROLL_STATES; ++i)
665 gtk_widget_remove_css_class (self->enroll_result_icon, ENROLL_STATE_CLASSES[i]);
666
667 gtk_widget_add_css_class (self->enroll_result_icon, ENROLL_STATE_CLASSES[enroll_state]);
668
669 gtk_image_set_from_icon_name (self->enroll_result_image, icon_name);
670 gtk_label_set_label (self->enroll_result_message, message);
671 }
672
673 static gboolean
674 stage_passed_timeout_cb (gpointer user_data)
675 {
676 CcFingerprintDialog *self = user_data;
677 const char *current_message;
678
679 current_message = gtk_label_get_label (self->enroll_result_message);
680 set_enroll_result_message (self, ENROLL_STATE_NORMAL, current_message);
681 self->enroll_stage_passed_id = 0;
682
683 return FALSE;
684 }
685
686 static void
687 handle_enroll_signal (CcFingerprintDialog *self,
688 const char *result,
689 gboolean done)
690 {
691 gboolean completed;
692
693 g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
694
695 g_debug ("Device enroll result message: %s, done: %d", result, done);
696
697 completed = g_str_equal (result, "enroll-completed");
698 g_clear_handle_id (&self->enroll_stage_passed_id, g_source_remove);
699
700 if (g_str_equal (result, "enroll-stage-passed") || completed)
701 {
702 guint enroll_stages;
703
704 enroll_stages = cc_fprintd_device_get_num_enroll_stages (self->device);
705
706 self->enroll_stages_passed++;
707
708 if (enroll_stages > 0)
709 self->enroll_progress =
710 MIN (1.0f, self->enroll_stages_passed / (double) enroll_stages);
711 else
712 g_warning ("The device %s requires an invalid number of enroll stages (%u)",
713 cc_fprintd_device_get_name (self->device), enroll_stages);
714
715 g_debug ("Enroll state passed, %u/%u (%.2f%%)",
716 self->enroll_stages_passed, (guint) enroll_stages,
717 self->enroll_progress);
718
719 if (!completed)
720 {
721 set_enroll_result_message (self, ENROLL_STATE_SUCCESS, NULL);
722
723 self->enroll_stage_passed_id =
724 g_timeout_add (750, stage_passed_timeout_cb, self);
725 }
726 else
727 {
728 if (!G_APPROX_VALUE (self->enroll_progress, 1.0f, FLT_EPSILON))
729 {
730 g_warning ("Device marked enroll as completed, but progress is at %.2f",
731 self->enroll_progress);
732 self->enroll_progress = 1.0f;
733 }
734 }
735 }
736 else if (!done)
737 {
738 const char *scan_type;
739 const char *message;
740 gboolean is_swipe;
741
742 scan_type = cc_fprintd_device_get_scan_type (self->device);
743 is_swipe = g_str_equal (scan_type, "swipe");
744
745 message = enroll_result_str_to_msg (result, is_swipe);
746 set_enroll_result_message (self, ENROLL_STATE_RETRY, message);
747
748 self->enroll_stage_passed_id =
749 g_timeout_add (850, stage_passed_timeout_cb, self);
750 }
751
752 if (done)
753 {
754 if (completed)
755 {
756 /* TRANSLATORS: This is the message shown when the fingerprint
757 * enrollment has been completed successfully */
758 set_enroll_result_message (self, ENROLL_STATE_COMPLETED,
759 C_("Fingerprint enroll state", "Complete"));
760 gtk_widget_set_sensitive (GTK_WIDGET (self->cancel_button), FALSE);
761 gtk_widget_set_sensitive (GTK_WIDGET (self->done_button), TRUE);
762 gtk_widget_grab_focus (GTK_WIDGET (self->done_button));
763 }
764 else
765 {
766 const char *message;
767
768 if (g_str_equal (result, "enroll-disconnected"))
769 {
770 message = _("Fingerprint device disconnected");
771 remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED |
772 DIALOG_STATE_DEVICE_ENROLLING);
773 }
774 else if (g_str_equal (result, "enroll-data-full"))
775 {
776 message = _("Fingerprint device storage is full");
777 }
778 else if (g_str_equal (result, "enroll-duplicate"))
779 {
780 message = _("Fingerprint is duplicate");
781 }
782 else
783 {
784 message = _("Failed to enroll new fingerprint");
785 }
786
787 set_enroll_result_message (self, ENROLL_STATE_WARNING, message);
788 }
789 }
790 }
791
792 static void
793 enroll_start_cb (GObject *object,
794 GAsyncResult *res,
795 gpointer user_data)
796 {
797 g_autoptr(GError) error = NULL;
798 g_autoptr(DialogStateRemover) state_remover = NULL;
799 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
800 CcFingerprintDialog *self = user_data;
801
802 cc_fprintd_device_call_enroll_start_finish (fprintd_device, res, &error);
803
804 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
805 return;
806
807 state_remover = auto_state_remover (self, DIALOG_STATE_DEVICE_ENROLL_STARTING);
808
809 if (error)
810 {
811 g_autofree char *error_message = NULL;
812
813 remove_dialog_state (self, DIALOG_STATE_DEVICE_ENROLLING);
814
815 error_message = g_strdup_printf (_("Failed to start enrollment: %s"),
816 dbus_error_to_human (self, error));
817 g_warning ("Enrollment on device %s failed: %s",
818 cc_fprintd_device_get_name (self->device), error->message);
819 notify_error (self, error_message);
820
821 set_enroll_result_message (self, ENROLL_STATE_ERROR,
822 C_("Fingerprint enroll state",
823 "Failed to enroll new fingerprint"));
824 gtk_widget_set_sensitive (self->enrollment_view, FALSE);
825
826 return;
827 }
828 }
829
830 static void
831 enroll_stop_cb (GObject *object,
832 GAsyncResult *res,
833 gpointer user_data)
834 {
835 g_autoptr(GError) error = NULL;
836 g_autoptr(DialogStateRemover) state_remover = NULL;
837 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
838 CcFingerprintDialog *self = user_data;
839
840 cc_fprintd_device_call_enroll_stop_finish (fprintd_device, res, &error);
841
842 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
843 return;
844
845 state_remover = auto_state_remover (self, DIALOG_STATE_DEVICE_ENROLLING |
846 DIALOG_STATE_DEVICE_ENROLL_STOPPING);
847 gtk_widget_set_sensitive (self->enrollment_view, TRUE);
848 gtk_stack_set_visible_child (self->stack, self->prints_manager);
849
850 if (error)
851 {
852 g_autofree char *error_message = NULL;
853
854 error_message = g_strdup_printf (_("Failed to stop enrollment: %s"),
855 dbus_error_to_human (self, error));
856 g_warning ("Stopping enrollment on device %s failed: %s",
857 cc_fprintd_device_get_name (self->device), error->message);
858 notify_error (self, error_message);
859
860 return;
861 }
862
863 cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
864 }
865
866 static void
867 enroll_stop (CcFingerprintDialog *self)
868 {
869 g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
870
871 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_ENROLL_STOPPING))
872 return;
873
874 gtk_widget_set_sensitive (self->enrollment_view, FALSE);
875 cc_fprintd_device_call_enroll_stop (self->device, self->cancellable,
876 enroll_stop_cb, self);
877 }
878
879 static char *
880 get_enrollment_string (CcFingerprintDialog *self,
881 const char *finger_id)
882 {
883 char *ret;
884 const char *scan_type;
885 const char *device_name;
886 gboolean is_swipe;
887
888 device_name = NULL;
889 scan_type = cc_fprintd_device_get_scan_type (self->device);
890 is_swipe = g_str_equal (scan_type, "swipe");
891
892 if (have_multiple_devices (self))
893 device_name = cc_fprintd_device_get_name (self->device);
894
895 ret = finger_str_to_msg (finger_id, device_name, is_swipe);
896
897 if (ret)
898 return ret;
899
900 return g_strdup (_("Repeatedly lift and place your finger on the reader to enroll your fingerprint"));
901 }
902
903 static void
904 enroll_finger (CcFingerprintDialog *self,
905 const char *finger_id)
906 {
907 g_auto(GStrv) tmp_finger_name = NULL;
908 g_autofree char *finger_name = NULL;
909 g_autofree char *enroll_message = NULL;
910
911 g_return_if_fail (finger_id);
912
913 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_ENROLLING |
914 DIALOG_STATE_DEVICE_ENROLL_STARTING))
915 return;
916
917 self->enroll_progress = 0;
918 self->enroll_stages_passed = 0;
919
920 g_debug ("Enrolling finger %s", finger_id);
921
922 enroll_message = get_enrollment_string (self, finger_id);
923 tmp_finger_name = g_strsplit (get_finger_name (finger_id), "_", -1);
924 finger_name = g_strjoinv ("", tmp_finger_name);
925
926 set_enroll_result_message (self, ENROLL_STATE_NORMAL, NULL);
927 gtk_stack_set_visible_child (self->stack, self->enrollment_view);
928 gtk_label_set_label (self->enroll_message, enroll_message);
929 gtk_editable_set_text (GTK_EDITABLE (self->enroll_print_entry), finger_name);
930
931 cc_fprintd_device_call_enroll_start (self->device, finger_id, self->cancellable,
932 enroll_start_cb, self);
933 }
934
935 static void
936 populate_enrollment_view (CcFingerprintDialog *self)
937 {
938 self->enroll_result_icon =
939 fingerprint_icon_new ("fingerprint-detection-symbolic",
940 NULL,
941 GTK_TYPE_IMAGE,
942 &self->enroll_progress,
943 (GtkWidget **) &self->enroll_result_image,
944 (GtkWidget **) &self->enroll_result_message);
945
946 gtk_box_prepend (GTK_BOX (self->enroll_print_bin), self->enroll_result_icon);
947
948 gtk_widget_add_css_class (self->enroll_result_icon, "enroll-status");
949 }
950
951 static void
952 on_print_activated_cb (CcFingerprintDialog *self,
953 GtkFlowBoxChild *child)
954 {
955 GtkWidget *selected_button;
956
957 selected_button = g_object_get_data (G_OBJECT (child), "button");
958 g_signal_emit_by_name (GTK_MENU_BUTTON (selected_button), "activate");
959 }
960
961 static void
962 on_enroll_cb (CcFingerprintDialog *self,
963 GtkMenuButton *button)
964 {
965 const char *finger_id;
966
967 finger_id = g_object_get_data (G_OBJECT (button), "finger-id");
968 enroll_finger (self, finger_id);
969 }
970
971 static void
972 populate_add_print_popover (CcFingerprintDialog *self)
973 {
974 guint i;
975
976 for (i = 0; i < N_VALID_FINGERS; ++i)
977 {
978 GtkWidget *finger_item;
979
980 finger_item = gtk_button_new ();
981 gtk_button_set_label (GTK_BUTTON (finger_item), get_finger_name (FINGER_IDS[i]));
982 gtk_button_set_use_underline (GTK_BUTTON (finger_item), TRUE);
983 g_object_set_data (G_OBJECT (finger_item), "finger-id", (gpointer) FINGER_IDS[i]);
984 gtk_box_prepend (GTK_BOX (self->add_print_popover_box), finger_item);
985
986 g_signal_connect_object (finger_item, "clicked", G_CALLBACK (on_enroll_cb),
987 self, G_CONNECT_SWAPPED);
988 }
989 }
990
991 static void
992 populate_prints_gallery (CcFingerprintDialog *self)
993 {
994 const char *add_print_label;
995 GtkWidget *button;
996 guint i;
997
998 g_return_if_fail (!GTK_IS_WIDGET (self->add_print_icon));
999
1000 for (i = 0; i < N_VALID_FINGERS; ++i)
1001 {
1002 GtkWidget *flowbox_child;
1003 GtkWidget *popover;
1004 GtkWidget *reenroll_button;
1005
1006 flowbox_child = fingerprint_menu_button ("fingerprint-detection-symbolic",
1007 get_finger_name (FINGER_IDS[i]));
1008
1009 button = g_object_get_data (G_OBJECT (flowbox_child), "button");
1010
1011 popover = gtk_popover_new ();
1012 reenroll_button = gtk_button_new ();
1013 gtk_button_set_use_underline (GTK_BUTTON (reenroll_button), TRUE);
1014 gtk_button_set_label (GTK_BUTTON (reenroll_button), _("_Re-enroll this fingerโ€ฆ"));
1015 g_object_set_data (G_OBJECT (reenroll_button), "finger-id",
1016 (gpointer) FINGER_IDS[i]);
1017 g_signal_connect_object (reenroll_button, "clicked", G_CALLBACK (on_enroll_cb), self, G_CONNECT_SWAPPED);
1018 gtk_popover_set_child (GTK_POPOVER (popover), reenroll_button);
1019
1020 gtk_menu_button_set_popover (GTK_MENU_BUTTON (button),
1021 popover);
1022 g_object_set_data (G_OBJECT (flowbox_child), "finger-id",
1023 (gpointer) FINGER_IDS[i]);
1024
1025 gtk_flow_box_insert (self->prints_gallery, flowbox_child, i);
1026 }
1027
1028 /* TRANSLATORS: This is the label for the button to enroll a new finger */
1029 add_print_label = _("Scan new fingerprint");
1030 self->add_print_icon = fingerprint_menu_button ("list-add-symbolic",
1031 add_print_label);
1032 gtk_widget_add_css_class (self->add_print_icon, "fingerprint-print-add");
1033
1034 populate_add_print_popover (self);
1035 button = g_object_get_data (G_OBJECT (self->add_print_icon), "button");
1036 gtk_menu_button_set_popover (GTK_MENU_BUTTON (button),
1037 GTK_WIDGET (self->add_print_popover));
1038
1039 gtk_flow_box_insert (self->prints_gallery, self->add_print_icon, -1);
1040 gtk_flow_box_set_max_children_per_line (self->prints_gallery, 1);
1041
1042 gtk_flow_box_set_filter_func (self->prints_gallery, prints_visibility_filter,
1043 self, NULL);
1044
1045 update_prints_visibility (self);
1046 }
1047
1048 static void
1049 release_device_cb (GObject *object,
1050 GAsyncResult *res,
1051 gpointer user_data)
1052 {
1053 g_autoptr(GError) error = NULL;
1054 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
1055 CcFingerprintDialog *self = user_data;
1056
1057 cc_fprintd_device_call_release_finish (fprintd_device, res, &error);
1058
1059 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1060 return;
1061
1062 if (error)
1063 {
1064 g_autofree char *error_message = NULL;
1065
1066 error_message = g_strdup_printf (_("Failed to release fingerprint device %s: %s"),
1067 cc_fprintd_device_get_name (fprintd_device),
1068 dbus_error_to_human (self, error));
1069 g_warning ("Releasing device %s failed: %s",
1070 cc_fprintd_device_get_name (self->device), error->message);
1071
1072 notify_error (self, error_message);
1073 return;
1074 }
1075
1076 remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED);
1077 }
1078
1079 static void
1080 release_device (CcFingerprintDialog *self)
1081 {
1082 if (!self->device || !(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
1083 return;
1084
1085 disconnect_device_signals (self);
1086
1087 cc_fprintd_device_call_release (self->device,
1088 self->cancellable,
1089 release_device_cb,
1090 self);
1091 }
1092
1093 static void
1094 on_device_signal (CcFingerprintDialog *self,
1095 gchar *sender_name,
1096 gchar *signal_name,
1097 GVariant *parameters,
1098 gpointer user_data)
1099 {
1100 if (g_str_equal (signal_name, "EnrollStatus"))
1101 {
1102 const char *result;
1103 gboolean done;
1104
1105 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sb)")))
1106 {
1107 g_warning ("Unexpected enroll parameters type %s",
1108 g_variant_get_type_string (parameters));
1109 return;
1110 }
1111
1112 g_variant_get (parameters, "(&sb)", &result, &done);
1113 handle_enroll_signal (self, result, done);
1114 }
1115 }
1116
1117 static void claim_device (CcFingerprintDialog *self);
1118
1119 static void
1120 on_device_owner_changed (CcFprintdDevice *device,
1121 GParamSpec *spec,
1122 CcFingerprintDialog *self)
1123 {
1124 g_autofree char *name_owner = NULL;
1125
1126 name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (device));
1127
1128 if (!name_owner)
1129 {
1130 if (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED)
1131 {
1132 disconnect_device_signals (self);
1133
1134 if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
1135 {
1136 set_enroll_result_message (self, ENROLL_STATE_ERROR,
1137 C_("Fingerprint enroll state",
1138 "Problem Reading Device"));
1139 }
1140
1141 remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED);
1142 claim_device (self);
1143 }
1144 }
1145 }
1146
1147 static void
1148 claim_device_cb (GObject *object,
1149 GAsyncResult *res,
1150 gpointer user_data)
1151 {
1152 g_autoptr(GError) error = NULL;
1153 g_autoptr(DialogStateRemover) state_remover = NULL;
1154 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
1155 CcFingerprintDialog *self = user_data;
1156
1157 cc_fprintd_device_call_claim_finish (fprintd_device, res, &error);
1158
1159 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1160 return;
1161
1162 state_remover = auto_state_remover (self, DIALOG_STATE_DEVICE_CLAIMING);
1163
1164 if (error)
1165 {
1166 g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
1167 g_autofree char *error_message = NULL;
1168
1169 if (dbus_error && g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.AlreadyInUse") &&
1170 (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
1171 return;
1172
1173 error_message = g_strdup_printf (_("Failed to claim fingerprint device %s: %s"),
1174 cc_fprintd_device_get_name (self->device),
1175 dbus_error_to_human (self, error));
1176 g_warning ("Claiming device %s failed: %s",
1177 cc_fprintd_device_get_name (self->device), error->message);
1178 notify_error (self, error_message);
1179 return;
1180 }
1181
1182 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED))
1183 return;
1184
1185 gtk_widget_set_sensitive (self->prints_manager, TRUE);
1186 self->device_signal_id = g_signal_connect_object (self->device, "g-signal",
1187 G_CALLBACK (on_device_signal),
1188 self, G_CONNECT_SWAPPED);
1189 self->device_name_owner_id = g_signal_connect_object (self->device, "notify::g-name-owner",
1190 G_CALLBACK (on_device_owner_changed),
1191 self, 0);
1192 }
1193
1194 static void
1195 claim_device (CcFingerprintDialog *self)
1196 {
1197 ActUser *user;
1198
1199 g_return_if_fail (!(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED));
1200
1201 if (!add_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMING))
1202 return;
1203
1204 user = cc_fingerprint_manager_get_user (self->manager);
1205 gtk_widget_set_sensitive (self->prints_manager, FALSE);
1206
1207 cc_fprintd_device_call_claim (self->device,
1208 act_user_get_user_name (user),
1209 self->cancellable,
1210 claim_device_cb,
1211 self);
1212 }
1213
1214 static void
1215 on_stack_child_changed (CcFingerprintDialog *self)
1216 {
1217 GtkWidget *visible_child = gtk_stack_get_visible_child (self->stack);
1218
1219 g_debug ("Fingerprint dialog child changed: %s",
1220 gtk_stack_get_visible_child_name (self->stack));
1221
1222 gtk_widget_set_visible (GTK_WIDGET (self->back_button), FALSE);
1223 gtk_widget_set_visible (GTK_WIDGET (self->cancel_button), FALSE);
1224 gtk_widget_set_visible (GTK_WIDGET (self->done_button), FALSE);
1225
1226 adw_header_bar_set_show_start_title_buttons (ADW_HEADER_BAR (self->titlebar), TRUE);
1227 adw_header_bar_set_show_end_title_buttons (ADW_HEADER_BAR (self->titlebar), TRUE);
1228 gtk_flow_box_invalidate_filter (self->prints_gallery);
1229
1230 if (visible_child == self->prints_manager)
1231 {
1232 gtk_widget_set_visible (GTK_WIDGET (self->back_button),
1233 have_multiple_devices (self));
1234 notify_error (self, NULL);
1235 update_prints_store (self);
1236
1237 if (!(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
1238 claim_device (self);
1239 }
1240 else if (visible_child == self->enrollment_view)
1241 {
1242 adw_header_bar_set_show_start_title_buttons (ADW_HEADER_BAR (self->titlebar), FALSE);
1243 adw_header_bar_set_show_end_title_buttons (ADW_HEADER_BAR (self->titlebar), FALSE);
1244
1245 gtk_widget_set_visible (GTK_WIDGET (self->cancel_button), TRUE);
1246 gtk_widget_set_sensitive (GTK_WIDGET (self->cancel_button), TRUE);
1247
1248 gtk_widget_set_visible (GTK_WIDGET (self->done_button), TRUE);
1249 gtk_widget_set_sensitive (GTK_WIDGET (self->done_button), FALSE);
1250 }
1251 else
1252 {
1253 release_device (self);
1254 g_clear_object (&self->device);
1255 }
1256 }
1257
1258 static void
1259 cc_fingerprint_dialog_init (CcFingerprintDialog *self)
1260 {
1261 self->cancellable = g_cancellable_new ();
1262
1263 gtk_widget_init_template (GTK_WIDGET (self));
1264
1265 on_stack_child_changed (self);
1266 g_signal_connect_object (self->stack, "notify::visible-child",
1267 G_CALLBACK (on_stack_child_changed), self,
1268 G_CONNECT_SWAPPED);
1269
1270 g_object_bind_property (self->stack, "visible-child-name",
1271 self->title, "label", G_BINDING_SYNC_CREATE);
1272
1273 populate_prints_gallery (self);
1274 populate_enrollment_view (self);
1275 }
1276
1277 static void
1278 select_device_row (CcFingerprintDialog *self,
1279 GtkListBoxRow *row,
1280 GtkListBox *listbox)
1281 {
1282 CcFprintdDevice *device = g_object_get_data (G_OBJECT (row), "device");
1283
1284 g_return_if_fail (CC_FPRINTD_DEVICE (device));
1285
1286 g_set_object (&self->device, device);
1287 gtk_stack_set_visible_child (self->stack, self->prints_manager);
1288 }
1289
1290 static void
1291 on_devices_list (GObject *object,
1292 GAsyncResult *res,
1293 gpointer user_data)
1294 {
1295 g_autolist (CcFprintdDevice) fprintd_devices = NULL;
1296 g_autoptr(DialogStateRemover) state_remover = NULL;
1297 g_autoptr(GError) error = NULL;
1298 CcFingerprintManager *fingerprint_manager = CC_FINGERPRINT_MANAGER (object);
1299 CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (user_data);
1300
1301 fprintd_devices = cc_fingerprint_manager_get_devices_finish (fingerprint_manager,
1302 res, &error);
1303
1304 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1305 return;
1306
1307 state_remover = auto_state_remover (self, DIALOG_STATE_DEVICES_LISTING);
1308
1309 if (fprintd_devices == NULL)
1310 {
1311 if (error)
1312 {
1313 g_autofree char *error_message = NULL;
1314
1315 error_message = g_strdup_printf (_("Failed to get fingerprint devices: %s"),
1316 dbus_error_to_human (self, error));
1317 g_warning ("Retrieving fingerprint devices failed: %s", error->message);
1318 notify_error (self, error_message);
1319 }
1320
1321 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->no_devices_found));
1322 }
1323 else if (fprintd_devices->next == NULL)
1324 {
1325 /* We have just one device... Skip devices selection */
1326 self->device = g_object_ref (fprintd_devices->data);
1327 gtk_stack_set_visible_child (self->stack, self->prints_manager);
1328 }
1329 else
1330 {
1331 GList *l;
1332
1333 for (l = fprintd_devices; l; l = l->next)
1334 {
1335 CcFprintdDevice *device = l->data;
1336 CcListRow *device_row;
1337
1338 device_row = g_object_new (CC_TYPE_LIST_ROW,
1339 "visible", TRUE,
1340 "icon-name", "go-next-symbolic",
1341 "title", cc_fprintd_device_get_name (device),
1342 NULL);
1343
1344 gtk_list_box_insert (self->devices_list, GTK_WIDGET (device_row), -1);
1345 g_object_set_data_full (G_OBJECT (device_row), "device",
1346 g_object_ref (device), g_object_unref);
1347 }
1348
1349 gtk_stack_set_visible_child (self->stack, self->device_selector);
1350 }
1351 }
1352
1353 static void
1354 cc_fingerprint_dialog_constructed (GObject *object)
1355 {
1356 CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
1357
1358 bindtextdomain ("fprintd", GNOMELOCALEDIR);
1359 bind_textdomain_codeset ("fprintd", "UTF-8");
1360
1361 add_dialog_state (self, DIALOG_STATE_DEVICES_LISTING);
1362 cc_fingerprint_manager_get_devices (self->manager, self->cancellable,
1363 on_devices_list, self);
1364 }
1365
1366 static void
1367 back_button_clicked_cb (CcFingerprintDialog *self)
1368 {
1369 if (gtk_stack_get_visible_child (self->stack) == self->prints_manager)
1370 {
1371 notify_error (self, NULL);
1372 gtk_stack_set_visible_child (self->stack, self->device_selector);
1373 return;
1374 }
1375
1376 g_return_if_reached ();
1377 }
1378
1379 static void
1380 confirm_deletion_button_clicked_cb (CcFingerprintDialog *self)
1381 {
1382 gtk_widget_set_visible (self->delete_confirmation_infobar, FALSE);
1383 delete_enrolled_prints (self);
1384 }
1385
1386 static void
1387 cancel_deletion_button_clicked_cb (CcFingerprintDialog *self)
1388 {
1389 gtk_widget_set_sensitive (self->prints_manager, TRUE);
1390 gtk_widget_set_visible (self->delete_confirmation_infobar, FALSE);
1391 }
1392
1393 static void
1394 delete_prints_button_clicked_cb (CcFingerprintDialog *self)
1395 {
1396 gtk_widget_set_sensitive (self->prints_manager, FALSE);
1397 gtk_widget_set_visible (self->delete_confirmation_infobar, TRUE);
1398 }
1399
1400 static void
1401 cancel_button_clicked_cb (CcFingerprintDialog *self)
1402 {
1403 if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
1404 {
1405 g_cancellable_cancel (self->cancellable);
1406 g_set_object (&self->cancellable, g_cancellable_new ());
1407
1408 g_debug ("Cancelling enroll operation");
1409 enroll_stop (self);
1410 }
1411 else
1412 {
1413 gtk_stack_set_visible_child (self->stack, self->prints_manager);
1414 }
1415 }
1416
1417 static void
1418 done_button_clicked_cb (CcFingerprintDialog *self)
1419 {
1420 g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
1421
1422 g_debug ("Completing enroll operation");
1423 enroll_stop (self);
1424 }
1425
1426 static gboolean
1427 cc_fingerprint_dialog_close_request (GtkWindow *window)
1428 {
1429 CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (window);
1430
1431 cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
1432
1433 g_clear_handle_id (&self->enroll_stage_passed_id, g_source_remove);
1434
1435 if (self->device && (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
1436 {
1437 disconnect_device_signals (self);
1438
1439 if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
1440 cc_fprintd_device_call_enroll_stop_sync (self->device, NULL, NULL);
1441 cc_fprintd_device_call_release (self->device, NULL, NULL, NULL);
1442 }
1443
1444 g_clear_object (&self->manager);
1445 g_clear_object (&self->device);
1446 g_clear_pointer (&self->enrolled_fingers, g_strfreev);
1447
1448 g_cancellable_cancel (self->cancellable);
1449 g_clear_object (&self->cancellable);
1450
1451 return GTK_WINDOW_CLASS (cc_fingerprint_dialog_parent_class)->close_request (window);
1452 }
1453
1454 static void
1455 cc_fingerprint_dialog_class_init (CcFingerprintDialogClass *klass)
1456 {
1457 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1458 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1459 GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass);
1460
1461 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL);
1462
1463 gtk_widget_class_set_template_from_resource (widget_class,
1464 "/org/gnome/control-center/system/users/cc-fingerprint-dialog.ui");
1465
1466 object_class->constructed = cc_fingerprint_dialog_constructed;
1467 object_class->get_property = cc_fingerprint_dialog_get_property;
1468 object_class->set_property = cc_fingerprint_dialog_set_property;
1469
1470 window_class->close_request = cc_fingerprint_dialog_close_request;
1471
1472 properties[PROP_MANAGER] =
1473 g_param_spec_object ("fingerprint-manager",
1474 "FingerprintManager",
1475 "The CC fingerprint manager",
1476 CC_TYPE_FINGERPRINT_MANAGER,
1477 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
1478
1479 g_object_class_install_properties (object_class, N_PROPS, properties);
1480
1481 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, add_print_popover);
1482 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, add_print_popover_box);
1483 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, back_button);
1484 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, cancel_button);
1485 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, delete_confirmation_infobar);
1486 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, delete_prints_button);
1487 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, device_selector);
1488 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, devices_list);
1489 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, done_button);
1490 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_message);
1491 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_print_bin);
1492 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_print_entry);
1493 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enrollment_view);
1494 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, error_infobar);
1495 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, infobar_error);
1496 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, no_devices_found);
1497 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, prints_gallery);
1498 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, prints_manager);
1499 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, spinner);
1500 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, stack);
1501 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, title);
1502 gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, titlebar);
1503
1504 gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb);
1505 gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
1506 gtk_widget_class_bind_template_callback (widget_class, cancel_deletion_button_clicked_cb);
1507 gtk_widget_class_bind_template_callback (widget_class, confirm_deletion_button_clicked_cb);
1508 gtk_widget_class_bind_template_callback (widget_class, delete_prints_button_clicked_cb);
1509 gtk_widget_class_bind_template_callback (widget_class, done_button_clicked_cb);
1510 gtk_widget_class_bind_template_callback (widget_class, on_print_activated_cb);
1511 gtk_widget_class_bind_template_callback (widget_class, select_device_row);
1512 }
1513