Use flags to track the dialog actions state so that we can: 1. Remove multiple flags to track specific states 2. Prevent starting again an action we're still waiting for completion 3. Handle global visual elements (such as the busy spinner) correctly Specifically point 3. allows to ensure that when doing concurrent actions such as prints listing and device claiming, we will stop the spinner only once all the operations are over.
1515 lines
48 KiB
C
1515 lines
48 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
|
*
|
|
* Copyright (C) 2020 Canonical Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Authors: Marco Trevisan <marco.trevisan@canonical.com>
|
|
*/
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <cairo/cairo.h>
|
|
|
|
#include "cc-fingerprint-dialog.h"
|
|
|
|
#include "cc-fingerprint-manager.h"
|
|
#include "cc-fprintd-generated.h"
|
|
#include "cc-list-row.h"
|
|
|
|
#include "config.h"
|
|
|
|
/* Translate fprintd strings */
|
|
#define TR(s) dgettext ("fprintd", s)
|
|
#include "fingerprint-strings.h"
|
|
|
|
typedef enum {
|
|
DIALOG_STATE_NONE = 0,
|
|
DIALOG_STATE_DEVICES_LISTING = (1 << 0),
|
|
DIALOG_STATE_DEVICE_CLAIMING = (1 << 1),
|
|
DIALOG_STATE_DEVICE_CLAIMED = (1 << 2),
|
|
DIALOG_STATE_DEVICE_PRINTS_LISTING = (1 << 3),
|
|
DIALOG_STATE_DEVICE_RELEASING = (1 << 4),
|
|
DIALOG_STATE_DEVICE_ENROLL_STARTING = (1 << 5),
|
|
DIALOG_STATE_DEVICE_ENROLLING = (1 << 6),
|
|
DIALOG_STATE_DEVICE_ENROLL_STOPPING = (1 << 7),
|
|
DIALOG_STATE_DEVICE_DELETING = (1 << 8),
|
|
|
|
DIALOG_STATE_IDLE = DIALOG_STATE_DEVICE_CLAIMED | DIALOG_STATE_DEVICE_ENROLLING,
|
|
} DialogState;
|
|
|
|
struct _CcFingerprintDialog
|
|
{
|
|
GtkWindow parent_instance;
|
|
|
|
GtkButton *back_button;
|
|
GtkButton *cancel_button;
|
|
GtkButton *delete_prints_button;
|
|
GtkButton *done_button;
|
|
GtkContainer *add_print_popover_box;
|
|
GtkEntry *enroll_print_entry;
|
|
GtkFlowBox *prints_gallery;
|
|
GtkHeaderBar *titlebar;
|
|
GtkImage *enroll_result_image;
|
|
GtkLabel *enroll_message;
|
|
GtkLabel *enroll_result_message;
|
|
GtkLabel *infobar_error;
|
|
GtkLabel *title;
|
|
GtkListBox *devices_list;
|
|
GtkPopoverMenu *add_print_popover;
|
|
GtkPopoverMenu *print_popover;
|
|
GtkSpinner *spinner;
|
|
GtkStack *stack;
|
|
GtkWidget *add_print_icon;
|
|
GtkWidget *delete_confirmation_infobar;
|
|
GtkWidget *device_selector;
|
|
GtkWidget *enroll_print_bin;
|
|
GtkWidget *enroll_result_icon;
|
|
GtkWidget *enrollment_view;
|
|
GtkWidget *error_infobar;
|
|
GtkWidget *no_devices_found;
|
|
GtkWidget *prints_manager;
|
|
|
|
CcFingerprintManager *manager;
|
|
DialogState dialog_state;
|
|
CcFprintdDevice *device;
|
|
gulong device_signal_id;
|
|
gulong device_name_owner_id;
|
|
GCancellable *cancellable;
|
|
GStrv enrolled_fingers;
|
|
guint enroll_stages_passed;
|
|
guint enroll_stage_passed_id;
|
|
gdouble enroll_progress;
|
|
};
|
|
|
|
/* TODO - fprintd and API changes required:
|
|
- Identify the finger when the enroll dialog is visible
|
|
+ Only if device supports identification
|
|
· And only in such case support enrolling more than one finger
|
|
- Delete a single fingerprint | and remove the "Delete all" button
|
|
- Highlight the finger when the sensor is touched during enrollment
|
|
- Add customized labels to fingerprints
|
|
- Devices hotplug (object manager)
|
|
*/
|
|
|
|
G_DEFINE_TYPE (CcFingerprintDialog, cc_fingerprint_dialog, GTK_TYPE_WINDOW)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_MANAGER,
|
|
N_PROPS
|
|
};
|
|
|
|
#define N_VALID_FINGERS G_N_ELEMENTS (FINGER_IDS) - 1
|
|
/* The order of the fingers here will affect the UI order */
|
|
const char * FINGER_IDS[] = {
|
|
"right-index-finger",
|
|
"left-index-finger",
|
|
"right-thumb",
|
|
"right-middle-finger",
|
|
"right-ring-finger",
|
|
"right-little-finger",
|
|
"left-thumb",
|
|
"left-middle-finger",
|
|
"left-ring-finger",
|
|
"left-little-finger",
|
|
"any",
|
|
};
|
|
|
|
typedef enum {
|
|
ENROLL_STATE_NORMAL,
|
|
ENROLL_STATE_RETRY,
|
|
ENROLL_STATE_SUCCESS,
|
|
ENROLL_STATE_WARNING,
|
|
ENROLL_STATE_ERROR,
|
|
ENROLL_STATE_COMPLETED,
|
|
N_ENROLL_STATES,
|
|
} EnrollState;
|
|
|
|
const char * ENROLL_STATE_CLASSES[N_ENROLL_STATES] = {
|
|
"",
|
|
"retry",
|
|
"success",
|
|
"warning",
|
|
"error",
|
|
"completed",
|
|
};
|
|
|
|
static GParamSpec *properties[N_PROPS];
|
|
|
|
CcFingerprintDialog *
|
|
cc_fingerprint_dialog_new (CcFingerprintManager *manager)
|
|
{
|
|
return g_object_new (CC_TYPE_FINGERPRINT_DIALOG,
|
|
"fingerprint-manager", manager,
|
|
NULL);
|
|
}
|
|
|
|
static gboolean
|
|
update_dialog_state (CcFingerprintDialog *self,
|
|
DialogState state)
|
|
{
|
|
if (self->dialog_state == state)
|
|
return FALSE;
|
|
|
|
self->dialog_state = state;
|
|
|
|
if (self->dialog_state == DIALOG_STATE_NONE ||
|
|
self->dialog_state == (self->dialog_state & DIALOG_STATE_IDLE))
|
|
{
|
|
gtk_spinner_stop (self->spinner);
|
|
}
|
|
else
|
|
{
|
|
gtk_spinner_start (self->spinner);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
add_dialog_state (CcFingerprintDialog *self,
|
|
DialogState state)
|
|
{
|
|
return update_dialog_state (self, (self->dialog_state | state));
|
|
}
|
|
|
|
static gboolean
|
|
remove_dialog_state (CcFingerprintDialog *self,
|
|
DialogState state)
|
|
{
|
|
return update_dialog_state (self, (self->dialog_state & ~state));
|
|
}
|
|
|
|
static void
|
|
disconnect_device_signals (CcFingerprintDialog *self)
|
|
{
|
|
if (!self->device)
|
|
return;
|
|
|
|
if (self->device_signal_id)
|
|
{
|
|
g_signal_handler_disconnect (self->device, self->device_signal_id);
|
|
self->device_signal_id = 0;
|
|
}
|
|
|
|
if (self->device_name_owner_id)
|
|
{
|
|
g_signal_handler_disconnect (self->device, self->device_name_owner_id);
|
|
self->device_name_owner_id = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_dispose (GObject *object)
|
|
{
|
|
CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
|
|
|
|
g_clear_handle_id (&self->enroll_stage_passed_id, g_source_remove);
|
|
|
|
if (self->device && (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
|
|
{
|
|
disconnect_device_signals (self);
|
|
|
|
if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
|
|
cc_fprintd_device_call_enroll_stop_sync (self->device, NULL, NULL);
|
|
cc_fprintd_device_call_release (self->device, NULL, NULL, NULL);
|
|
}
|
|
|
|
g_clear_object (&self->manager);
|
|
g_clear_object (&self->device);
|
|
g_clear_pointer (&self->enrolled_fingers, g_strfreev);
|
|
|
|
g_cancellable_cancel (self->cancellable);
|
|
g_clear_object (&self->cancellable);
|
|
|
|
G_OBJECT_CLASS (cc_fingerprint_dialog_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MANAGER:
|
|
g_value_set_object (value, self->manager);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MANAGER:
|
|
g_set_object (&self->manager, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
notify_error (CcFingerprintDialog *self,
|
|
const char *error_message)
|
|
{
|
|
if (error_message)
|
|
gtk_label_set_label (self->infobar_error, error_message);
|
|
|
|
gtk_widget_set_visible (self->error_infobar, error_message != NULL);
|
|
}
|
|
|
|
static gboolean
|
|
fingerprint_icon_draw (GtkWidget *widget,
|
|
cairo_t *cr,
|
|
gdouble *progress_data)
|
|
{
|
|
gdouble progress = 0.0f;
|
|
|
|
if (progress_data)
|
|
progress = *progress_data;
|
|
|
|
if (G_APPROX_VALUE (progress, 0.f, FLT_EPSILON) || progress > 1)
|
|
return FALSE;
|
|
|
|
GTK_WIDGET_GET_CLASS (widget)->draw (widget, cr);
|
|
|
|
if (progress > 0)
|
|
{
|
|
g_autoptr(GdkRGBA) outline_color = NULL;
|
|
GtkStyleContext *context;
|
|
GtkStateFlags state;
|
|
int outline_width;
|
|
int outline_offset;
|
|
int width;
|
|
int height;
|
|
int radius;
|
|
int delta;
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
gtk_style_context_save (context);
|
|
|
|
state = gtk_style_context_get_state (context);
|
|
|
|
gtk_style_context_add_class (context, "progress");
|
|
gtk_style_context_get (context, state,
|
|
"outline-width", &outline_width,
|
|
"outline-offset", &outline_offset,
|
|
"outline-color", &outline_color,
|
|
NULL);
|
|
|
|
width = gtk_widget_get_allocated_width (widget);
|
|
height = gtk_widget_get_allocated_height (widget);
|
|
radius = MIN (width / 2, height / 2) + outline_offset;
|
|
delta = radius - outline_width / 2;
|
|
|
|
cairo_arc (cr, width / 2., height / 2., delta,
|
|
1.5 * G_PI, (1.5 + progress * 2) * G_PI);
|
|
gdk_cairo_set_source_rgba (cr, outline_color);
|
|
|
|
cairo_set_line_width (cr, MIN (outline_width, radius));
|
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
|
|
cairo_stroke (cr);
|
|
|
|
gtk_style_context_restore (context);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GtkWidget *
|
|
fingerprint_icon_new (const char *icon_name,
|
|
const char *label_text,
|
|
GType icon_widget_type,
|
|
gpointer progress_data,
|
|
GtkWidget **out_icon,
|
|
GtkWidget **out_label)
|
|
{
|
|
GtkStyleContext *context;
|
|
GtkWidget *box;
|
|
GtkWidget *label;
|
|
GtkWidget *image;
|
|
GtkWidget *icon_widget;
|
|
|
|
g_return_val_if_fail (g_type_is_a (icon_widget_type, GTK_TYPE_WIDGET), NULL);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
|
gtk_widget_set_name (box, "fingerprint-box");
|
|
gtk_widget_set_hexpand (box, TRUE);
|
|
|
|
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DND);
|
|
|
|
if (icon_widget_type == GTK_TYPE_IMAGE)
|
|
icon_widget = image;
|
|
else
|
|
icon_widget = g_object_new (icon_widget_type, NULL);
|
|
|
|
if (progress_data)
|
|
g_signal_connect (image, "draw", G_CALLBACK (fingerprint_icon_draw),
|
|
progress_data);
|
|
|
|
if (g_type_is_a (icon_widget_type, GTK_TYPE_BUTTON))
|
|
{
|
|
gtk_button_set_image (GTK_BUTTON (icon_widget), image);
|
|
gtk_button_set_relief (GTK_BUTTON (icon_widget), GTK_RELIEF_NONE);
|
|
gtk_widget_set_can_focus (icon_widget, FALSE);
|
|
}
|
|
|
|
gtk_widget_set_halign (icon_widget, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (icon_widget, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_name (icon_widget, "fingerprint-image");
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), icon_widget);
|
|
|
|
context = gtk_widget_get_style_context (icon_widget);
|
|
gtk_style_context_add_class (context, "fingerprint-image");
|
|
|
|
label = gtk_label_new_with_mnemonic (label_text);
|
|
gtk_container_add (GTK_CONTAINER (box), label);
|
|
|
|
context = gtk_widget_get_style_context (box);
|
|
gtk_style_context_add_class (context, "fingerprint-icon");
|
|
|
|
if (out_icon)
|
|
*out_icon = icon_widget;
|
|
|
|
if (out_label)
|
|
*out_label = label;
|
|
|
|
return box;
|
|
}
|
|
|
|
static GtkWidget *
|
|
fingerprint_menu_button (const char *icon_name,
|
|
const char *label_text)
|
|
{
|
|
GtkWidget *flowbox_child;
|
|
GtkWidget *button;
|
|
GtkWidget *label;
|
|
GtkWidget *box;
|
|
|
|
box = fingerprint_icon_new (icon_name, label_text, GTK_TYPE_MENU_BUTTON, NULL,
|
|
&button, &label);
|
|
|
|
flowbox_child = gtk_flow_box_child_new ();
|
|
gtk_widget_set_focus_on_click (flowbox_child, FALSE);
|
|
gtk_widget_set_name (flowbox_child, "fingerprint-flowbox");
|
|
|
|
gtk_container_add (GTK_CONTAINER (flowbox_child), box);
|
|
|
|
g_object_set_data (G_OBJECT (flowbox_child), "button", button);
|
|
g_object_set_data (G_OBJECT (flowbox_child), "icon",
|
|
gtk_button_get_image (GTK_BUTTON (button)));
|
|
g_object_set_data (G_OBJECT (flowbox_child), "label", label);
|
|
g_object_set_data (G_OBJECT (button), "flowbox-child", flowbox_child);
|
|
|
|
return flowbox_child;
|
|
}
|
|
|
|
static gboolean
|
|
prints_visibility_filter (GtkFlowBoxChild *child,
|
|
gpointer user_data)
|
|
{
|
|
CcFingerprintDialog *self = user_data;
|
|
const char *finger_id;
|
|
|
|
if (gtk_stack_get_visible_child (self->stack) != self->prints_manager)
|
|
return FALSE;
|
|
|
|
finger_id = g_object_get_data (G_OBJECT (child), "finger-id");
|
|
|
|
if (!finger_id)
|
|
return TRUE;
|
|
|
|
if (!self->enrolled_fingers)
|
|
return FALSE;
|
|
|
|
return g_strv_contains ((const gchar **) self->enrolled_fingers, finger_id);
|
|
}
|
|
|
|
static void
|
|
update_prints_to_add_visibility (CcFingerprintDialog *self)
|
|
{
|
|
g_autoptr(GList) print_buttons = NULL;
|
|
GList *l;
|
|
guint i;
|
|
|
|
print_buttons = gtk_container_get_children (self->add_print_popover_box);
|
|
|
|
for (i = 0, l = print_buttons; i < N_VALID_FINGERS && l; ++i, l = l->next)
|
|
{
|
|
GtkWidget *button = l->data;
|
|
gboolean enrolled;
|
|
|
|
enrolled = self->enrolled_fingers &&
|
|
g_strv_contains ((const gchar **) self->enrolled_fingers,
|
|
FINGER_IDS[i]);
|
|
|
|
gtk_widget_set_visible (button, !enrolled);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_prints_visibility (CcFingerprintDialog *self)
|
|
{
|
|
update_prints_to_add_visibility (self);
|
|
|
|
gtk_flow_box_invalidate_filter (self->prints_gallery);
|
|
}
|
|
|
|
static void
|
|
list_enrolled_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_auto(GStrv) enrolled_fingers = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
guint n_enrolled_fingers = 0;
|
|
|
|
cc_fprintd_device_call_list_enrolled_fingers_finish (fprintd_device,
|
|
&enrolled_fingers,
|
|
res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_PRINTS_LISTING);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->add_print_icon), TRUE);
|
|
|
|
if (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED)
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->prints_manager), TRUE);
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
|
|
|
|
if (!dbus_error || !g_str_has_suffix (dbus_error, ".Error.NoEnrolledPrints"))
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to list fingerprints: %s"),
|
|
error->message);
|
|
g_warning ("Listing of fingerprints on device %s failed: %s",
|
|
cc_fprintd_device_get_name (self->device), error->message);
|
|
notify_error (self, error_message);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n_enrolled_fingers = g_strv_length (enrolled_fingers);
|
|
}
|
|
|
|
self->enrolled_fingers = g_steal_pointer (&enrolled_fingers);
|
|
gtk_flow_box_set_max_children_per_line (self->prints_gallery,
|
|
MIN (3, n_enrolled_fingers + 1));
|
|
|
|
update_prints_visibility (self);
|
|
|
|
if (n_enrolled_fingers == N_VALID_FINGERS)
|
|
gtk_widget_set_sensitive (self->add_print_icon, FALSE);
|
|
|
|
if (n_enrolled_fingers > 0)
|
|
gtk_widget_show (GTK_WIDGET (self->delete_prints_button));
|
|
}
|
|
|
|
static void
|
|
update_prints_store (CcFingerprintDialog *self)
|
|
{
|
|
ActUser *user;
|
|
|
|
g_assert_true (CC_FPRINTD_IS_DEVICE (self->device));
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_PRINTS_LISTING))
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->add_print_icon), FALSE);
|
|
gtk_widget_hide (GTK_WIDGET (self->delete_prints_button));
|
|
|
|
g_clear_pointer (&self->enrolled_fingers, g_strfreev);
|
|
|
|
user = cc_fingerprint_manager_get_user (self->manager);
|
|
cc_fprintd_device_call_list_enrolled_fingers (self->device,
|
|
act_user_get_user_name (user),
|
|
self->cancellable,
|
|
list_enrolled_cb,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
delete_prints_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
|
|
cc_fprintd_device_call_delete_enrolled_fingers2_finish (fprintd_device, res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to delete saved fingerprints: %s"),
|
|
error->message);
|
|
g_warning ("Deletion of fingerprints on device %s failed: %s",
|
|
cc_fprintd_device_get_name (self->device), error->message);
|
|
notify_error (self, error_message);
|
|
}
|
|
|
|
update_prints_store (self);
|
|
cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
delete_enrolled_prints (CcFingerprintDialog *self)
|
|
{
|
|
g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED);
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_DELETING))
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->prints_manager), FALSE);
|
|
|
|
cc_fprintd_device_call_delete_enrolled_fingers2 (self->device,
|
|
self->cancellable,
|
|
delete_prints_cb,
|
|
self);
|
|
}
|
|
|
|
static const char *
|
|
get_finger_name (const char *finger_id)
|
|
{
|
|
if (g_str_equal (finger_id, "left-thumb"))
|
|
return _("Left thumb");
|
|
if (g_str_equal (finger_id, "left-middle-finger"))
|
|
return _("Left middle finger");
|
|
if (g_str_equal (finger_id, "left-index-finger"))
|
|
return _("_Left index finger");
|
|
if (g_str_equal (finger_id, "left-ring-finger"))
|
|
return _("Left ring finger");
|
|
if (g_str_equal (finger_id, "left-little-finger"))
|
|
return _("Left little finger");
|
|
if (g_str_equal (finger_id, "right-thumb"))
|
|
return _("Right thumb");
|
|
if (g_str_equal (finger_id, "right-middle-finger"))
|
|
return _("Right middle finger");
|
|
if (g_str_equal (finger_id, "right-index-finger"))
|
|
return _("_Right index finger");
|
|
if (g_str_equal (finger_id, "right-ring-finger"))
|
|
return _("Right ring finger");
|
|
if (g_str_equal (finger_id, "right-little-finger"))
|
|
return _("Right little finger");
|
|
|
|
g_return_val_if_reached (_("Unknown Finger"));
|
|
}
|
|
|
|
static gboolean
|
|
have_multiple_devices (CcFingerprintDialog *self)
|
|
{
|
|
g_autoptr(GList) devices_rows = NULL;
|
|
|
|
devices_rows = gtk_container_get_children (GTK_CONTAINER (self->devices_list));
|
|
|
|
return devices_rows && devices_rows->next;
|
|
}
|
|
|
|
static void
|
|
set_enroll_result_message (CcFingerprintDialog *self,
|
|
EnrollState enroll_state,
|
|
const char *message)
|
|
{
|
|
GtkStyleContext *style_context;
|
|
const char *icon_name;
|
|
guint i;
|
|
|
|
g_return_if_fail (enroll_state >= 0 && enroll_state < N_ENROLL_STATES);
|
|
|
|
style_context = gtk_widget_get_style_context (self->enroll_result_icon);
|
|
|
|
switch (enroll_state)
|
|
{
|
|
case ENROLL_STATE_WARNING:
|
|
case ENROLL_STATE_ERROR:
|
|
icon_name = "fingerprint-detection-warning-symbolic";
|
|
break;
|
|
case ENROLL_STATE_COMPLETED:
|
|
icon_name = "fingerprint-detection-complete-symbolic";
|
|
break;
|
|
default:
|
|
icon_name = "fingerprint-detection-symbolic";
|
|
}
|
|
|
|
for (i = 0; i < N_ENROLL_STATES; ++i)
|
|
gtk_style_context_remove_class (style_context, ENROLL_STATE_CLASSES[i]);
|
|
|
|
gtk_style_context_add_class (style_context, ENROLL_STATE_CLASSES[enroll_state]);
|
|
|
|
gtk_image_set_from_icon_name (self->enroll_result_image, icon_name, GTK_ICON_SIZE_DND);
|
|
gtk_label_set_label (self->enroll_result_message, message);
|
|
}
|
|
|
|
static gboolean
|
|
stage_passed_timeout_cb (gpointer user_data)
|
|
{
|
|
CcFingerprintDialog *self = user_data;
|
|
const char *current_message;
|
|
|
|
current_message = gtk_label_get_label (self->enroll_result_message);
|
|
set_enroll_result_message (self, ENROLL_STATE_NORMAL, current_message);
|
|
self->enroll_stage_passed_id = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
handle_enroll_signal (CcFingerprintDialog *self,
|
|
const char *result,
|
|
gboolean done)
|
|
{
|
|
gboolean completed;
|
|
|
|
g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
|
|
|
|
g_debug ("Device enroll result message: %s, done: %d", result, done);
|
|
|
|
completed = g_str_equal (result, "enroll-completed");
|
|
g_clear_handle_id (&self->enroll_stage_passed_id, g_source_remove);
|
|
|
|
if (g_str_equal (result, "enroll-stage-passed") || completed)
|
|
{
|
|
guint enroll_stages;
|
|
|
|
enroll_stages = cc_fprintd_device_get_num_enroll_stages (self->device);
|
|
|
|
self->enroll_stages_passed++;
|
|
|
|
if (enroll_stages > 0)
|
|
self->enroll_progress =
|
|
MIN (1.0f, self->enroll_stages_passed / (double) enroll_stages);
|
|
else
|
|
g_warning ("The device %s requires an invalid number of enroll stages (%u)",
|
|
cc_fprintd_device_get_name (self->device), enroll_stages);
|
|
|
|
g_debug ("Enroll state passed, %u/%u (%.2f%%)",
|
|
self->enroll_stages_passed, (guint) enroll_stages,
|
|
self->enroll_progress);
|
|
|
|
if (!completed)
|
|
{
|
|
set_enroll_result_message (self, ENROLL_STATE_SUCCESS, NULL);
|
|
|
|
self->enroll_stage_passed_id =
|
|
g_timeout_add (750, stage_passed_timeout_cb, self);
|
|
}
|
|
else
|
|
{
|
|
if (!G_APPROX_VALUE (self->enroll_progress, 1.0f, FLT_EPSILON))
|
|
{
|
|
g_warning ("Device marked enroll as completed, but progress is at %.2f",
|
|
self->enroll_progress);
|
|
self->enroll_progress = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else if (!done)
|
|
{
|
|
const char *scan_type;
|
|
const char *message;
|
|
gboolean is_swipe;
|
|
|
|
scan_type = cc_fprintd_device_get_scan_type (self->device);
|
|
is_swipe = g_str_equal (scan_type, "swipe");
|
|
|
|
message = enroll_result_str_to_msg (result, is_swipe);
|
|
set_enroll_result_message (self, ENROLL_STATE_RETRY, message);
|
|
|
|
self->enroll_stage_passed_id =
|
|
g_timeout_add (850, stage_passed_timeout_cb, self);
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
if (completed)
|
|
{
|
|
/* TRANSLATORS: This is the message shown when the fingerprint
|
|
* enrollment has been completed successfully */
|
|
set_enroll_result_message (self, ENROLL_STATE_COMPLETED,
|
|
C_("Fingerprint enroll state", "Complete"));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->cancel_button), FALSE);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->done_button), TRUE);
|
|
gtk_widget_grab_focus (GTK_WIDGET (self->done_button));
|
|
}
|
|
else
|
|
{
|
|
const char *message;
|
|
|
|
if (g_str_equal (result, "enroll-disconnected"))
|
|
message = _("Fingerprint device disconnected");
|
|
else if (g_str_equal (result, "enroll-data-full"))
|
|
message = _("Fingerprint device storage is full");
|
|
else
|
|
message = _("Failed to enroll new fingerprint");
|
|
|
|
set_enroll_result_message (self, ENROLL_STATE_WARNING, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
enroll_start_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
|
|
cc_fprintd_device_call_enroll_start_finish (fprintd_device, res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_ENROLL_STARTING);
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_ENROLLING);
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to start enrollment: %s"),
|
|
error->message);
|
|
g_warning ("Enrollment on device %s failed: %s",
|
|
cc_fprintd_device_get_name (self->device), error->message);
|
|
notify_error (self, error_message);
|
|
|
|
set_enroll_result_message (self, ENROLL_STATE_ERROR,
|
|
C_("Fingerprint enroll state",
|
|
"Failed to enroll new fingerprint"));
|
|
gtk_widget_set_sensitive (self->enrollment_view, FALSE);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
enroll_stop_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
|
|
cc_fprintd_device_call_enroll_stop_finish (fprintd_device, res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_ENROLLING |
|
|
DIALOG_STATE_DEVICE_ENROLL_STOPPING);
|
|
gtk_widget_set_sensitive (self->enrollment_view, TRUE);
|
|
gtk_stack_set_visible_child (self->stack, self->prints_manager);
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to stop enrollment: %s"),
|
|
error->message);
|
|
g_warning ("Stopping enrollment on device %s failed: %s",
|
|
cc_fprintd_device_get_name (self->device), error->message);
|
|
notify_error (self, error_message);
|
|
|
|
return;
|
|
}
|
|
|
|
cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
enroll_stop (CcFingerprintDialog *self)
|
|
{
|
|
g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_ENROLL_STOPPING))
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (self->enrollment_view, FALSE);
|
|
cc_fprintd_device_call_enroll_stop (self->device, self->cancellable,
|
|
enroll_stop_cb, self);
|
|
}
|
|
|
|
static char *
|
|
get_enrollment_string (CcFingerprintDialog *self,
|
|
const char *finger_id)
|
|
{
|
|
char *ret;
|
|
const char *scan_type;
|
|
const char *device_name;
|
|
gboolean is_swipe;
|
|
|
|
device_name = NULL;
|
|
scan_type = cc_fprintd_device_get_scan_type (self->device);
|
|
is_swipe = g_str_equal (scan_type, "swipe");
|
|
|
|
if (have_multiple_devices (self))
|
|
device_name = cc_fprintd_device_get_name (self->device);
|
|
|
|
ret = finger_str_to_msg (finger_id, device_name, is_swipe);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return g_strdup (_("Repeatedly lift and place your finger on the reader to enroll your fingerprint"));
|
|
}
|
|
|
|
static void
|
|
enroll_finger (CcFingerprintDialog *self,
|
|
const char *finger_id)
|
|
{
|
|
g_auto(GStrv) tmp_finger_name = NULL;
|
|
g_autofree char *finger_name = NULL;
|
|
g_autofree char *enroll_message = NULL;
|
|
|
|
g_return_if_fail (finger_id);
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_ENROLLING |
|
|
DIALOG_STATE_DEVICE_ENROLL_STARTING))
|
|
return;
|
|
|
|
self->enroll_progress = 0;
|
|
self->enroll_stages_passed = 0;
|
|
|
|
g_debug ("Enrolling finger %s", finger_id);
|
|
|
|
enroll_message = get_enrollment_string (self, finger_id);
|
|
tmp_finger_name = g_strsplit (get_finger_name (finger_id), "_", -1);
|
|
finger_name = g_strjoinv ("", tmp_finger_name);
|
|
|
|
set_enroll_result_message (self, ENROLL_STATE_NORMAL, NULL);
|
|
gtk_stack_set_visible_child (self->stack, self->enrollment_view);
|
|
gtk_label_set_label (self->enroll_message, enroll_message);
|
|
gtk_entry_set_text (self->enroll_print_entry, finger_name);
|
|
|
|
cc_fprintd_device_call_enroll_start (self->device, finger_id, self->cancellable,
|
|
enroll_start_cb, self);
|
|
}
|
|
|
|
static void
|
|
populate_enrollment_view (CcFingerprintDialog *self)
|
|
{
|
|
GtkStyleContext *style_context;
|
|
|
|
self->enroll_result_icon =
|
|
fingerprint_icon_new ("fingerprint-detection-symbolic",
|
|
NULL,
|
|
GTK_TYPE_IMAGE,
|
|
&self->enroll_progress,
|
|
(GtkWidget **) &self->enroll_result_image,
|
|
(GtkWidget **) &self->enroll_result_message);
|
|
|
|
gtk_container_add (GTK_CONTAINER (self->enroll_print_bin), self->enroll_result_icon);
|
|
|
|
style_context = gtk_widget_get_style_context (self->enroll_result_icon);
|
|
gtk_style_context_add_class (style_context, "enroll-status");
|
|
|
|
gtk_widget_show_all (self->enroll_print_bin);
|
|
}
|
|
|
|
static void
|
|
reenroll_finger_cb (CcFingerprintDialog *self)
|
|
{
|
|
GtkWidget *button;
|
|
GtkWidget *flowbox_child;
|
|
const char *finger_id;
|
|
|
|
button = gtk_popover_get_relative_to (GTK_POPOVER (self->print_popover));
|
|
flowbox_child = g_object_get_data (G_OBJECT (button), "flowbox-child");
|
|
finger_id = g_object_get_data (G_OBJECT (flowbox_child), "finger-id");
|
|
|
|
enroll_finger (self, finger_id);
|
|
}
|
|
|
|
static void
|
|
on_print_activated_cb (GtkFlowBox *flowbox,
|
|
GtkFlowBoxChild *child,
|
|
CcFingerprintDialog *self)
|
|
{
|
|
GtkWidget *selected_button;
|
|
|
|
selected_button = g_object_get_data (G_OBJECT (child), "button");
|
|
gtk_button_clicked (GTK_BUTTON (selected_button));
|
|
}
|
|
|
|
static void
|
|
on_enroll_cb (CcFingerprintDialog *self,
|
|
GtkModelButton *button)
|
|
{
|
|
const char *finger_id;
|
|
|
|
finger_id = g_object_get_data (G_OBJECT (button), "finger-id");
|
|
enroll_finger (self, finger_id);
|
|
}
|
|
|
|
static void
|
|
populate_add_print_popover (CcFingerprintDialog *self)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < N_VALID_FINGERS; ++i)
|
|
{
|
|
GtkWidget *finger_item;
|
|
|
|
finger_item = gtk_model_button_new ();
|
|
gtk_button_set_label (GTK_BUTTON (finger_item), get_finger_name (FINGER_IDS[i]));
|
|
gtk_button_set_use_underline (GTK_BUTTON (finger_item), TRUE);
|
|
g_object_set_data (G_OBJECT (finger_item), "finger-id", (gpointer) FINGER_IDS[i]);
|
|
gtk_container_add (self->add_print_popover_box, finger_item);
|
|
|
|
g_signal_connect_object (finger_item, "clicked", G_CALLBACK (on_enroll_cb),
|
|
self, G_CONNECT_SWAPPED);
|
|
}
|
|
}
|
|
|
|
static void
|
|
populate_prints_gallery (CcFingerprintDialog *self)
|
|
{
|
|
const char *add_print_label;
|
|
GtkWidget *button;
|
|
GtkStyleContext *style_context;
|
|
guint i;
|
|
|
|
g_return_if_fail (!GTK_IS_WIDGET (self->add_print_icon));
|
|
|
|
for (i = 0; i < N_VALID_FINGERS; ++i)
|
|
{
|
|
GtkWidget *flowbox_child;
|
|
|
|
flowbox_child = fingerprint_menu_button ("fingerprint-detection-symbolic",
|
|
get_finger_name (FINGER_IDS[i]));
|
|
|
|
button = g_object_get_data (G_OBJECT (flowbox_child), "button");
|
|
|
|
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button),
|
|
GTK_WIDGET (self->print_popover));
|
|
/* Move the popover on click, so we can just reuse the same instance */
|
|
g_signal_connect_object (button, "clicked",
|
|
G_CALLBACK (gtk_popover_set_relative_to),
|
|
self->print_popover, G_CONNECT_SWAPPED);
|
|
|
|
g_object_set_data (G_OBJECT (flowbox_child), "finger-id",
|
|
(gpointer) FINGER_IDS[i]);
|
|
|
|
gtk_flow_box_insert (self->prints_gallery, flowbox_child, i);
|
|
}
|
|
|
|
/* TRANSLATORS: This is the label for the button to enroll a new finger */
|
|
add_print_label = _("Scan new fingerprint");
|
|
self->add_print_icon = fingerprint_menu_button ("list-add-symbolic",
|
|
add_print_label);
|
|
style_context = gtk_widget_get_style_context (self->add_print_icon);
|
|
gtk_style_context_add_class (style_context, "fingerprint-print-add");
|
|
|
|
populate_add_print_popover (self);
|
|
button = g_object_get_data (G_OBJECT (self->add_print_icon), "button");
|
|
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button),
|
|
GTK_WIDGET (self->add_print_popover));
|
|
|
|
gtk_flow_box_insert (self->prints_gallery, self->add_print_icon, -1);
|
|
gtk_flow_box_set_max_children_per_line (self->prints_gallery, 1);
|
|
|
|
gtk_widget_show_all (GTK_WIDGET (self->prints_gallery));
|
|
gtk_flow_box_set_filter_func (self->prints_gallery, prints_visibility_filter,
|
|
self, NULL);
|
|
|
|
update_prints_visibility (self);
|
|
}
|
|
|
|
static void
|
|
release_device_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
|
|
cc_fprintd_device_call_release_finish (fprintd_device, res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to release fingerprint device %s: %s"),
|
|
cc_fprintd_device_get_name (fprintd_device),
|
|
error->message);
|
|
g_warning ("%s", error_message);
|
|
|
|
notify_error (self, error_message);
|
|
return;
|
|
}
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED);
|
|
}
|
|
|
|
static void
|
|
release_device (CcFingerprintDialog *self)
|
|
{
|
|
if (!self->device || !(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
|
|
return;
|
|
|
|
disconnect_device_signals (self);
|
|
|
|
cc_fprintd_device_call_release (self->device,
|
|
self->cancellable,
|
|
release_device_cb,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
on_device_signal (CcFingerprintDialog *self,
|
|
gchar *sender_name,
|
|
gchar *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
if (g_str_equal (signal_name, "EnrollStatus"))
|
|
{
|
|
const char *result;
|
|
gboolean done;
|
|
|
|
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sb)")))
|
|
{
|
|
g_warning ("Unexpected enroll parameters type %s",
|
|
g_variant_get_type_string (parameters));
|
|
return;
|
|
}
|
|
|
|
g_variant_get (parameters, "(&sb)", &result, &done);
|
|
handle_enroll_signal (self, result, done);
|
|
}
|
|
}
|
|
|
|
static void claim_device (CcFingerprintDialog *self);
|
|
|
|
static void
|
|
on_device_owner_changed (CcFprintdDevice *device,
|
|
GParamSpec *spec,
|
|
CcFingerprintDialog *self)
|
|
{
|
|
g_autofree char *name_owner = NULL;
|
|
|
|
name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (device));
|
|
|
|
if (!name_owner)
|
|
{
|
|
if (self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED)
|
|
{
|
|
disconnect_device_signals (self);
|
|
|
|
if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
|
|
{
|
|
set_enroll_result_message (self, ENROLL_STATE_ERROR,
|
|
C_("Fingerprint enroll state",
|
|
"Problem Reading Device"));
|
|
}
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED);
|
|
claim_device (self);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
claim_device_cb (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
|
|
CcFingerprintDialog *self = user_data;
|
|
|
|
cc_fprintd_device_call_claim_finish (fprintd_device, res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMING);
|
|
|
|
if (error)
|
|
{
|
|
g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
|
|
g_autofree char *error_message = NULL;
|
|
|
|
if (dbus_error && g_str_has_suffix (dbus_error, ".Error.AlreadyInUse") &&
|
|
(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
|
|
return;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to claim fingerprint device %s: %s"),
|
|
cc_fprintd_device_get_name (self->device),
|
|
error->message);
|
|
g_warning ("%s", error_message);
|
|
notify_error (self, error_message);
|
|
return;
|
|
}
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMED))
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (self->prints_manager, TRUE);
|
|
self->device_signal_id = g_signal_connect_object (self->device, "g-signal",
|
|
G_CALLBACK (on_device_signal),
|
|
self, G_CONNECT_SWAPPED);
|
|
self->device_name_owner_id = g_signal_connect_object (self->device, "notify::g-name-owner",
|
|
G_CALLBACK (on_device_owner_changed),
|
|
self, 0);
|
|
}
|
|
|
|
static void
|
|
claim_device (CcFingerprintDialog *self)
|
|
{
|
|
ActUser *user;
|
|
|
|
g_return_if_fail (!(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED));
|
|
|
|
if (!add_dialog_state (self, DIALOG_STATE_DEVICE_CLAIMING))
|
|
return;
|
|
|
|
user = cc_fingerprint_manager_get_user (self->manager);
|
|
gtk_widget_set_sensitive (self->prints_manager, FALSE);
|
|
|
|
cc_fprintd_device_call_claim (self->device,
|
|
act_user_get_user_name (user),
|
|
self->cancellable,
|
|
claim_device_cb,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
on_stack_child_changed (CcFingerprintDialog *self)
|
|
{
|
|
GtkWidget *visible_child = gtk_stack_get_visible_child (self->stack);
|
|
|
|
g_debug ("Fingerprint dialog child changed: %s",
|
|
gtk_stack_get_visible_child_name (self->stack));
|
|
|
|
gtk_widget_hide (GTK_WIDGET (self->back_button));
|
|
gtk_widget_hide (GTK_WIDGET (self->cancel_button));
|
|
gtk_widget_hide (GTK_WIDGET (self->done_button));
|
|
|
|
gtk_header_bar_set_show_close_button (self->titlebar, TRUE);
|
|
gtk_flow_box_invalidate_filter (self->prints_gallery);
|
|
|
|
if (visible_child == self->prints_manager)
|
|
{
|
|
gtk_widget_set_visible (GTK_WIDGET (self->back_button),
|
|
have_multiple_devices (self));
|
|
notify_error (self, NULL);
|
|
update_prints_store (self);
|
|
|
|
if (!(self->dialog_state & DIALOG_STATE_DEVICE_CLAIMED))
|
|
claim_device (self);
|
|
}
|
|
else if (visible_child == self->enrollment_view)
|
|
{
|
|
gtk_header_bar_set_show_close_button (self->titlebar, FALSE);
|
|
|
|
gtk_widget_show (GTK_WIDGET (self->cancel_button));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->cancel_button), TRUE);
|
|
|
|
gtk_widget_show (GTK_WIDGET (self->done_button));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->done_button), FALSE);
|
|
}
|
|
else
|
|
{
|
|
release_device (self);
|
|
g_clear_object (&self->device);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_init (CcFingerprintDialog *self)
|
|
{
|
|
g_autoptr(GtkCssProvider) provider = NULL;
|
|
|
|
self->cancellable = g_cancellable_new ();
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
|
|
provider = gtk_css_provider_new ();
|
|
gtk_css_provider_load_from_resource (provider,
|
|
"/org/gnome/control-center/user-accounts/cc-fingerprint-dialog.css");
|
|
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
|
GTK_STYLE_PROVIDER (provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
|
|
on_stack_child_changed (self);
|
|
g_signal_connect_object (self->stack, "notify::visible-child",
|
|
G_CALLBACK (on_stack_child_changed), self,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
g_object_bind_property (self->stack, "visible-child-name",
|
|
self->title, "label", G_BINDING_SYNC_CREATE);
|
|
|
|
populate_prints_gallery (self);
|
|
populate_enrollment_view (self);
|
|
}
|
|
|
|
static void
|
|
select_device_row (CcFingerprintDialog *self,
|
|
GtkListBoxRow *row,
|
|
GtkListBox *listbox)
|
|
{
|
|
CcFprintdDevice *device = g_object_get_data (G_OBJECT (row), "device");
|
|
|
|
g_return_if_fail (CC_FPRINTD_DEVICE (device));
|
|
|
|
g_set_object (&self->device, device);
|
|
gtk_stack_set_visible_child (self->stack, self->prints_manager);
|
|
}
|
|
|
|
static void
|
|
on_devices_list (GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
g_autolist (CcFprintdDevice) fprintd_devices = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
CcFingerprintManager *fingerprint_manager = CC_FINGERPRINT_MANAGER (object);
|
|
CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (user_data);
|
|
|
|
fprintd_devices = cc_fingerprint_manager_get_devices_finish (fingerprint_manager,
|
|
res, &error);
|
|
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
remove_dialog_state (self, DIALOG_STATE_DEVICES_LISTING);
|
|
|
|
if (fprintd_devices == NULL)
|
|
{
|
|
if (error)
|
|
{
|
|
g_autofree char *error_message = NULL;
|
|
|
|
g_dbus_error_strip_remote_error (error);
|
|
error_message = g_strdup_printf (_("Failed to get fingerprint devices: %s"),
|
|
error->message);
|
|
g_warning ("%s", error_message);
|
|
notify_error (self, error_message);
|
|
}
|
|
|
|
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->no_devices_found));
|
|
}
|
|
else if (fprintd_devices->next == NULL)
|
|
{
|
|
/* We have just one device... Skip devices selection */
|
|
self->device = g_object_ref (fprintd_devices->data);
|
|
gtk_stack_set_visible_child (self->stack, self->prints_manager);
|
|
}
|
|
else
|
|
{
|
|
GList *l;
|
|
|
|
for (l = fprintd_devices; l; l = l->next)
|
|
{
|
|
CcFprintdDevice *device = l->data;
|
|
CcListRow *device_row;
|
|
|
|
device_row = g_object_new (CC_TYPE_LIST_ROW,
|
|
"visible", TRUE,
|
|
"icon-name", "go-next-symbolic",
|
|
"title", cc_fprintd_device_get_name (device),
|
|
NULL);
|
|
|
|
gtk_list_box_insert (self->devices_list, GTK_WIDGET (device_row), -1);
|
|
g_object_set_data_full (G_OBJECT (device_row), "device",
|
|
g_object_ref (device), g_object_unref);
|
|
}
|
|
|
|
gtk_stack_set_visible_child (self->stack, self->device_selector);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_constructed (GObject *object)
|
|
{
|
|
CcFingerprintDialog *self = CC_FINGERPRINT_DIALOG (object);
|
|
|
|
bindtextdomain ("fprintd", GNOMELOCALEDIR);
|
|
bind_textdomain_codeset ("fprintd", "UTF-8");
|
|
|
|
add_dialog_state (self, DIALOG_STATE_DEVICES_LISTING);
|
|
cc_fingerprint_manager_get_devices (self->manager, self->cancellable,
|
|
on_devices_list, self);
|
|
}
|
|
|
|
static void
|
|
back_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
if (gtk_stack_get_visible_child (self->stack) == self->prints_manager)
|
|
{
|
|
notify_error (self, NULL);
|
|
gtk_stack_set_visible_child (self->stack, self->device_selector);
|
|
return;
|
|
}
|
|
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
static void
|
|
confirm_deletion_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
gtk_widget_hide (self->delete_confirmation_infobar);
|
|
delete_enrolled_prints (self);
|
|
}
|
|
|
|
static void
|
|
cancel_deletion_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
gtk_widget_set_sensitive (self->prints_manager, TRUE);
|
|
gtk_widget_hide (self->delete_confirmation_infobar);
|
|
}
|
|
|
|
static void
|
|
delete_prints_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
gtk_widget_set_sensitive (self->prints_manager, FALSE);
|
|
gtk_widget_show (self->delete_confirmation_infobar);
|
|
}
|
|
|
|
static void
|
|
cancel_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
if (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING)
|
|
{
|
|
g_cancellable_cancel (self->cancellable);
|
|
g_set_object (&self->cancellable, g_cancellable_new ());
|
|
|
|
g_debug ("Cancelling enroll operation");
|
|
enroll_stop (self);
|
|
}
|
|
else
|
|
{
|
|
gtk_stack_set_visible_child (self->stack, self->prints_manager);
|
|
}
|
|
}
|
|
|
|
static void
|
|
done_button_clicked_cb (CcFingerprintDialog *self)
|
|
{
|
|
g_return_if_fail (self->dialog_state & DIALOG_STATE_DEVICE_ENROLLING);
|
|
|
|
g_debug ("Completing enroll operation");
|
|
enroll_stop (self);
|
|
}
|
|
|
|
static void
|
|
fingerprint_dialog_delete_cb (CcFingerprintDialog *self)
|
|
{
|
|
cc_fingerprint_manager_update_state (self->manager, NULL, NULL);
|
|
gtk_widget_destroy (GTK_WIDGET (self));
|
|
}
|
|
|
|
static void
|
|
cc_fingerprint_dialog_class_init (CcFingerprintDialogClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class,
|
|
"/org/gnome/control-center/user-accounts/cc-fingerprint-dialog.ui");
|
|
|
|
object_class->constructed = cc_fingerprint_dialog_constructed;
|
|
object_class->dispose = cc_fingerprint_dialog_dispose;
|
|
object_class->get_property = cc_fingerprint_dialog_get_property;
|
|
object_class->set_property = cc_fingerprint_dialog_set_property;
|
|
|
|
properties[PROP_MANAGER] =
|
|
g_param_spec_object ("fingerprint-manager",
|
|
"FingerprintManager",
|
|
"The CC fingerprint manager",
|
|
CC_TYPE_FINGERPRINT_MANAGER,
|
|
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, properties);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, add_print_popover);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, add_print_popover_box);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, back_button);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, cancel_button);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, delete_confirmation_infobar);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, delete_prints_button);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, device_selector);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, devices_list);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, done_button);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_message);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_print_bin);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enroll_print_entry);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, enrollment_view);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, error_infobar);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, infobar_error);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, no_devices_found);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, print_popover);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, prints_gallery);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, prints_manager);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, spinner);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, stack);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, title);
|
|
gtk_widget_class_bind_template_child (widget_class, CcFingerprintDialog, titlebar);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, cancel_deletion_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, confirm_deletion_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, delete_prints_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, done_button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, fingerprint_dialog_delete_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, on_print_activated_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, reenroll_finger_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, select_device_row);
|
|
}
|