Implement Multi-Device

This patch implements multi-device support:

- The account creation wizzard has now two options "Existing Ring
  account" and "New Ring account".

  "Existing Ring account": Allows for fetching a Ring account archive
  from the DHT. Requires pin and password.

  "New Ring account": This is the previously existing wizard. It was
  modified to ask for a password which will be used to encrypt the
  account archive. This password is then required for exporting the
  account on the Ring.

- Creating a new Ring account with the "+" button now triggers the
  account creation wizard.

- The account menu now has a "devices" tab. This tab contains a table
  with device names (currently a short hash) and device ids (a long
  hash).

  In the "devices" tab, there is an "add device" button which allows
  for exporting the current account to the Ring, giving a pin to the
  user.

- When the client encounters old-format accounts, it will trigger a
  migration popup which asks the user for a password. This password will
  be used to create an archive and encrypt it. One password will be
  asked for each Ring account to migrate.

Change-Id: I3d52b2b7ca4f82cb477ee294c962b5d50d5c6a04
Tuleap: #896
diff --git a/src/accountcreationwizard.cpp b/src/accountcreationwizard.cpp
new file mode 100644
index 0000000..507f47e
--- /dev/null
+++ b/src/accountcreationwizard.cpp
@@ -0,0 +1,553 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+
+// GTK+ related
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+// LRC
+#include <account.h>
+#include <codecmodel.h>
+#include <profilemodel.h>
+#include <profile.h>
+#include <accountmodel.h>
+#include <personmodel.h>
+
+// Ring Client
+#include "utils/models.h"
+#include "avatarmanipulation.h"
+#include "accountcreationwizard.h"
+
+
+struct _AccountCreationWizard
+{
+    GtkBox parent;
+};
+
+struct _AccountCreationWizardClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _AccountCreationWizardPrivate AccountCreationWizardPrivate;
+
+struct _AccountCreationWizardPrivate
+{
+    GtkWidget *stack_account_creation;
+    QMetaObject::Connection account_state_changed;
+
+    /* choose_account_type_vbox */
+    GtkWidget *choose_account_type_vbox;
+    GtkWidget *choose_account_type_ring_logo;
+    GtkWidget *button_new_account;
+    GtkWidget *button_existing_account;
+    GtkWidget *button_wizard_cancel;
+
+    /* existing account */
+    GtkWidget *existing_account;
+    GtkWidget *button_existing_account_next;
+    GtkWidget *button_existing_account_previous;
+    GtkWidget *entry_existing_account_pin;
+    GtkWidget *entry_existing_account_password;
+    GtkWidget *retrieving_account;
+
+    /* account creation */
+    GtkWidget *account_creation;
+    GtkWidget *vbox_account_creation_entry;
+    GtkWidget *entry_alias;
+    GtkWidget *entry_password;
+    GtkWidget *entry_password_confirm;
+    GtkWidget *label_default_name;
+    GtkWidget *label_paceholder;
+    GtkWidget *button_account_creation_next;
+    GtkWidget *button_account_creation_previous;
+    GtkWidget *box_avatarselection;
+    GtkWidget *avatar_manipulation;
+    GtkWidget *label_password_error;
+
+    /* generating_account_spinner */
+    GtkWidget *vbox_generating_account_spinner;
+
+    /* error_view */
+    GtkWidget *error_view;
+    GtkWidget *button_error_view_ok;
+
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountCreationWizard, account_creation_wizard, GTK_TYPE_BOX);
+
+#define ACCOUNT_CREATION_WIZARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardPrivate))
+
+/* signals */
+enum {
+    ACCOUNT_CREATION_CANCELED,
+    ACCOUNT_CREATION_COMPLETED,
+    LAST_SIGNAL
+};
+
+static guint account_creation_wizard_signals[LAST_SIGNAL] = { 0 };
+
+static void
+destroy_avatar_manipulation(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    /* make sure the AvatarManipulation widget is destroyed so the VideoWidget inside of it is too;
+     * NOTE: destorying its parent (box_avatarselection) first will cause a mystery 'BadDrawable'
+     * crash due to X error */
+    if (priv->avatar_manipulation)
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
+        priv->avatar_manipulation = nullptr;
+    }
+}
+
+static void
+account_creation_wizard_dispose(GObject *object)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(object);
+    destroy_avatar_manipulation(ACCOUNT_CREATION_WIZARD(object));
+    QObject::disconnect(priv->account_state_changed);
+    G_OBJECT_CLASS(account_creation_wizard_parent_class)->dispose(object);
+}
+
+static void
+account_creation_wizard_init(AccountCreationWizard *view)
+{
+    gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_creation_wizard_class_init(AccountCreationWizardClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = account_creation_wizard_dispose;
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+                                                "/cx/ring/RingGnome/accountcreationwizard.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, stack_account_creation);
+
+    /* choose_account_type_vbox */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_vbox);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_ring_logo);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_wizard_cancel);
+
+    /* existing account */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account_next);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account_previous);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, retrieving_account);
+
+    /* account creation */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, account_creation);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_account_creation_entry);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_alias);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password_confirm);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_default_name);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_paceholder);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_next);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_previous);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_avatarselection);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_password_error);
+
+    /* generating_account_spinner */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_generating_account_spinner);
+
+    /* error view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, error_view);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_error_view_ok);
+
+    /* add signals */
+    account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED] = g_signal_new("account-creation-completed",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+    account_creation_wizard_signals[ACCOUNT_CREATION_CANCELED] = g_signal_new("account-creation-canceled",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+}
+
+static void
+show_error_view(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->error_view);
+}
+
+static gboolean
+create_ring_account(AccountCreationWizard *view,
+                    gchar *alias,
+                    gchar *password,
+                    gchar *pin)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    g_object_ref(view); // ref so its not desroyed too early
+
+    /* create account and set UPnP enabled, as its not by default in the daemon */
+    Account *account = nullptr;
+
+    /* get profile (if so) */
+    auto profile = ProfileModel::instance().selectedProfile();
+
+    if (alias && strlen(alias) > 0) {
+        account = AccountModel::instance().add(alias, Account::Protocol::RING);
+        if(profile && AccountModel::instance().size() == 1)
+        {
+            profile->person()->setFormattedName(alias);
+        }
+    } else {
+        auto unknown_alias = C_("The default username / account alias, if none is set by the user", "Unknown");
+        account = AccountModel::instance().add(unknown_alias, Account::Protocol::RING);
+        if (profile && AccountModel::instance().size() == 1)
+        {
+            profile->person()->setFormattedName(unknown_alias);
+        }
+    }
+
+    /* Set the archive password */
+    account->setArchivePassword(password);
+
+    /* Set the archive pin (existng accounts) */
+    if(pin)
+    {
+        account->setArchivePin(pin);
+    }
+
+    account->setDisplayName(alias); // set the display name to the same as the alias
+
+    account->setUpnpEnabled(TRUE);
+
+    /* show error window if the account errors */
+    priv->account_state_changed = QObject::connect(
+        account,
+        &Account::stateChanged,
+        [=] (Account::RegistrationState state) {
+            switch(state)
+            {
+                case Account::RegistrationState::ERROR:
+                {
+                    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+                    QObject::disconnect(priv->account_state_changed);
+                    show_error_view(view);
+                    g_object_unref(view);
+                    break;
+                }
+                case Account::RegistrationState::READY:
+                case Account::RegistrationState::TRYING:
+                case Account::RegistrationState::UNREGISTERED:
+                {
+                    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+                    QObject::disconnect(priv->account_state_changed);
+
+                    account << Account::EditAction::RELOAD;
+                    g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
+                    g_object_unref(view);
+                    break;
+                }
+                case Account::RegistrationState::INITIALIZING:
+                case Account::RegistrationState::COUNT__:
+                {
+                    // Keep waiting...
+                    break;
+                }
+            }
+        }
+    );
+
+    account->performAction(Account::EditAction::SAVE);
+    profile->save();
+
+    return G_SOURCE_REMOVE;
+}
+
+static gboolean
+create_new_ring_account(AccountCreationWizard *win)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    gchar *alias = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_alias)));
+
+    gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_password_confirm), "");
+
+    auto status = create_ring_account(win, alias, password, NULL);
+
+    g_free(alias);
+    g_free(password);
+
+    return status;
+}
+
+static gboolean
+create_existing_ring_account(AccountCreationWizard *win)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
+
+    gchar *pin = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
+
+    auto status = create_ring_account(win, NULL, password, pin);
+
+    g_free(password);
+    g_free(pin);
+
+    return status;
+}
+
+
+static void
+alias_entry_changed(GtkEditable *entry, AccountCreationWizard *win)
+{
+    g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(win));
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (!alias || strlen(alias) == 0) {
+        gtk_widget_show(priv->label_default_name);
+        gtk_widget_hide(priv->label_paceholder);
+    } else {
+        gtk_widget_hide(priv->label_default_name);
+        gtk_widget_show(priv->label_paceholder);
+    }
+}
+
+static void
+show_generating_account_spinner(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->vbox_generating_account_spinner);
+}
+
+static void
+account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    /* Check for correct password */
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (g_strcmp0(password, password_confirm) != 0)
+    {
+        gtk_widget_show(priv->label_password_error);
+        return;
+    }
+
+    show_generating_account_spinner(win);
+
+    /* now create account after a short timeout so that the the save doesn't
+     * happen freeze the client before the widget changes happen;
+     * the timeout function should then display the next step in account creation
+     */
+    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_new_ring_account, win, NULL);
+}
+
+static void
+show_retrieving_account(AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->retrieving_account);
+}
+
+static void
+existing_account_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
+{
+    show_retrieving_account(win);
+
+    /* now create account after a short timeout so that the the save doesn't
+     * happen freeze the client before the widget changes happen;
+     * the timeout function should then display the next step in account creation
+     */
+    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_existing_ring_account, win, NULL);
+}
+
+static void
+entry_alias_activated(GtkEntry *entry, AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (strlen(alias) > 0)
+        gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
+}
+
+
+static void
+show_choose_account_type(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->choose_account_type_vbox);
+}
+
+static void
+show_existing_account(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
+}
+
+static void
+show_account_creation(AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    /* avatar manipulation widget */
+    if (!priv->avatar_manipulation)
+    {
+        priv->avatar_manipulation = avatar_manipulation_new_from_wizard();
+        gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
+    }
+
+    gtk_widget_hide(priv->label_password_error);
+
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->account_creation);
+}
+
+static void
+wizard_cancel_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
+{
+    g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_CANCELED], 0);
+}
+
+static void
+entries_existing_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    const gchar *pin = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin));
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password));
+
+    if (strlen(pin) > 0 && strlen(password) > 0)
+    {
+        gtk_widget_set_sensitive(priv->button_existing_account_next, TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(priv->button_existing_account_next, FALSE);
+    }
+}
+
+static void
+entries_new_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (strlen(password) > 0 && strlen(password_confirm) > 0)
+    {
+        gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
+    }
+}
+
+static void
+build_creation_wizard_view(AccountCreationWizard *view, bool show_cancel_button)
+{
+    g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    /* set ring logo */
+    GError *error = NULL;
+    GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
+                                                                  -1, 50, TRUE, &error);
+    if (logo_ring == NULL) {
+        g_debug("Could not load logo: %s", error->message);
+        g_clear_error(&error);
+    } else
+        gtk_image_set_from_pixbuf(GTK_IMAGE(priv->choose_account_type_ring_logo), logo_ring);
+
+
+    /* use the real name / username of the logged in user as the default */
+    const char* real_name = g_get_real_name();
+    const char* user_name = g_get_user_name();
+    g_debug("real_name = %s",real_name);
+    g_debug("user_name = %s",user_name);
+
+    /* check first if the real name was determined */
+    if (g_strcmp0 (real_name,"Unknown") != 0)
+        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), real_name);
+    else
+        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), user_name);
+
+    /* cancel button */
+    gtk_widget_set_visible(priv->button_wizard_cancel, show_cancel_button);
+
+    /* choose_account_type signals */
+    g_signal_connect_swapped(priv->button_new_account, "clicked", G_CALLBACK(show_account_creation), view);
+    g_signal_connect_swapped(priv->button_existing_account, "clicked", G_CALLBACK(show_existing_account), view);
+    g_signal_connect(priv->button_wizard_cancel, "clicked", G_CALLBACK(wizard_cancel_clicked), view);
+
+    /* account_creation signals */
+    g_signal_connect_swapped(priv->button_account_creation_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
+    g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), view);
+    g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), view);
+    g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), view);
+    g_signal_connect(priv->entry_password, "changed", G_CALLBACK(entries_new_account_changed), view);
+    g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(entries_new_account_changed), view);
+
+    /* existing_account singals */
+    g_signal_connect_swapped(priv->button_existing_account_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
+    g_signal_connect(priv->button_existing_account_next, "clicked", G_CALLBACK(existing_account_next_clicked), view);
+    g_signal_connect(priv->entry_existing_account_pin, "changed", G_CALLBACK(entries_existing_account_changed), view);
+    g_signal_connect(priv->entry_existing_account_password, "changed", G_CALLBACK(entries_existing_account_changed), view);
+
+    /* error_view signals */
+    g_signal_connect_swapped(priv->button_error_view_ok, "clicked", G_CALLBACK(show_choose_account_type), view);
+
+    show_choose_account_type(view);
+}
+
+GtkWidget *
+account_creation_wizard_new(bool show_cancel_button)
+{
+    gpointer view = g_object_new(ACCOUNT_CREATION_WIZARD_TYPE, NULL);
+
+    build_creation_wizard_view(ACCOUNT_CREATION_WIZARD(view), show_cancel_button);
+    return (GtkWidget *)view;
+}
diff --git a/src/accountcreationwizard.h b/src/accountcreationwizard.h
new file mode 100644
index 0000000..c47d86c
--- /dev/null
+++ b/src/accountcreationwizard.h
@@ -0,0 +1,38 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_CREATION_WIZARD_TYPE            (account_creation_wizard_get_type ())
+#define ACCOUNT_CREATION_WIZARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizard))
+#define ACCOUNT_CREATION_WIZARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardClass))
+#define IS_ACCOUNT_CREATION_WIZARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_CREATION_WIZARD_TYPE))
+#define IS_ACCOUNT_CREATION_WIZARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_CREATION_WIZARD_TYPE))
+
+typedef struct _AccountCreationWizard      AccountCreationWizard;
+typedef struct _AccountCreationWizardClass AccountCreationWizardClass;
+
+GType      account_creation_wizard_get_type      (void) G_GNUC_CONST;
+GtkWidget *account_creation_wizard_new           (bool cancel_button);
+
+G_END_DECLS
diff --git a/src/accountdevicestab.cpp b/src/accountdevicestab.cpp
new file mode 100644
index 0000000..fbf8f18
--- /dev/null
+++ b/src/accountdevicestab.cpp
@@ -0,0 +1,272 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+// GTK+ related
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+// LRC
+#include <account.h>
+#include <codecmodel.h>
+#include "ringdevicemodel.h"
+#include "ringdevice.h"
+
+// Ring Client
+#include "accountdevicestab.h"
+#include "models/gtkqtreemodel.h"
+#include "utils/models.h"
+
+struct _AccountDevicesTab
+{
+    GtkBox parent;
+};
+
+struct _AccountDevicesTabClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _AccountDevicesTabPrivate AccountDevicesTabPrivate;
+
+struct _AccountDevicesTabPrivate
+{
+    Account   *account;
+    GtkWidget *stack_account_devices;
+    QMetaObject::Connection export_on_ring_ended;
+
+    /* generated_pin view */
+    GtkWidget *generated_pin;
+    GtkWidget *label_generated_pin;
+    GtkWidget *button_generated_pin_ok;
+
+    /* manage_devices view */
+    GtkWidget *manage_devices;
+    GtkWidget *label_device_id;
+    GtkWidget *treeview_devices;
+    GtkWidget *button_add_device;
+
+    /* add_device view */
+    GtkWidget *add_device;
+    GtkWidget *button_export_on_the_ring;
+    GtkWidget *button_add_device_cancel;
+    GtkWidget *entry_password;
+
+    /* generating account spinner */
+    GtkWidget *vbox_generating_pin_spinner;
+
+    /* export on ring error */
+    GtkWidget *export_on_ring_error;
+    GtkWidget *label_export_on_ring_error;
+    GtkWidget* button_export_on_ring_error_ok;
+
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountDevicesTab, account_devices_tab, GTK_TYPE_BOX);
+
+#define ACCOUNT_DEVICES_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabPrivate))
+
+static void
+account_devices_tab_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(account_devices_tab_parent_class)->dispose(object);
+}
+
+static void
+account_devices_tab_init(AccountDevicesTab *view)
+{
+    gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_devices_tab_class_init(AccountDevicesTabClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = account_devices_tab_dispose;
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+                                                "/cx/ring/RingGnome/accountdevicestab.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, stack_account_devices);
+
+    /* generated_pin view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, generated_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_generated_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_generated_pin_ok);
+
+    /* manage_devices view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, manage_devices);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_device_id);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, treeview_devices);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device);
+
+    /* add_device view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, add_device);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_the_ring);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device_cancel);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, entry_password);
+
+    /* generating pin spinner */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, vbox_generating_pin_spinner);
+
+    /* export on ring error */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, export_on_ring_error);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_export_on_ring_error);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_ring_error_ok);
+}
+
+static void
+show_manage_devices_view(AccountDevicesTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
+}
+
+static void
+show_add_device_view(AccountDevicesTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->add_device);
+}
+
+static void
+show_generated_pin_view(AccountDevicesTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    gtk_widget_show(priv->generated_pin);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->generated_pin);
+}
+
+static void
+show_generating_pin_spinner(AccountDevicesTab *view)
+{
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->vbox_generating_pin_spinner);
+}
+
+static void
+show_export_on_ring_error(AccountDevicesTab *view)
+{
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->export_on_ring_error);
+}
+
+static void
+export_on_the_ring_clicked(G_GNUC_UNUSED GtkButton *button, AccountDevicesTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+
+    auto password = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
+
+    priv->export_on_ring_ended = QObject::connect(
+        priv->account,
+        &Account::exportOnRingEnded,
+        [=] (Account::ExportOnRingStatus status, QString pin) {
+            QObject::disconnect(priv->export_on_ring_ended);
+            switch (status)
+            {
+                case Account::ExportOnRingStatus::SUCCESS:
+                {
+                    gtk_label_set_text(GTK_LABEL(priv->label_generated_pin), pin.toStdString().c_str());
+                    show_generated_pin_view(view);
+                    break;
+                }
+                case Account::ExportOnRingStatus::WRONG_PASSWORD:
+                {
+                    gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Bad password"));
+                    show_export_on_ring_error(view);
+                    break;
+                }
+                case Account::ExportOnRingStatus::NETWORK_ERROR:
+                {
+                    gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Network error, try again"));
+                    show_export_on_ring_error(view);
+                    break;
+                }
+            }
+        }
+    );
+
+    show_generating_pin_spinner(view);
+    if (!priv->account->exportOnRing(password))
+    {
+        QObject::disconnect(priv->export_on_ring_ended);
+        gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Could not initiate export to the Ring, try again"));
+        g_debug("Could not initiate exportOnRing operation");
+        show_export_on_ring_error(view);
+    }
+}
+
+static void
+build_tab_view(AccountDevicesTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+
+    g_signal_connect_swapped(priv->button_add_device, "clicked", G_CALLBACK(show_add_device_view), view);
+    g_signal_connect_swapped(priv->button_add_device_cancel, "clicked", G_CALLBACK(show_manage_devices_view), view);
+    g_signal_connect(priv->button_export_on_the_ring, "clicked", G_CALLBACK(export_on_the_ring_clicked), view);
+    g_signal_connect_swapped(priv->button_generated_pin_ok, "clicked", G_CALLBACK(show_manage_devices_view), view);
+    g_signal_connect_swapped(priv->button_export_on_ring_error_ok, "clicked", G_CALLBACK(show_add_device_view), view);
+
+    gtk_label_set_text(GTK_LABEL(priv->label_device_id), priv->account->deviceId().toUtf8().constData());
+
+    /* treeview_devices */
+    auto *devices_model = gtk_q_tree_model_new(
+        (QAbstractItemModel*) priv->account->ringDeviceModel(),
+        2,
+        RingDevice::Column::Name, Qt::DisplayRole, G_TYPE_STRING,
+        RingDevice::Column::Id, Qt::DisplayRole, G_TYPE_STRING);
+
+    gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_devices), GTK_TREE_MODEL(devices_model));
+
+    GtkCellRenderer* renderer;
+    GtkTreeViewColumn* column;
+
+    renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes(C_("Device Name Column", "Name"), renderer, "text", 0, NULL);
+    gtk_tree_view_column_set_expand(column, TRUE);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
+
+    renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes(C_("Device ID Column", "ID"), renderer, "text", 1, NULL);
+    gtk_tree_view_column_set_expand(column, TRUE);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
+
+    /* Show manage-devices view */
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
+}
+
+GtkWidget *
+account_devices_tab_new(Account *account)
+{
+    g_return_val_if_fail(account != NULL, NULL);
+
+    gpointer view = g_object_new(ACCOUNT_DEVICES_TAB_TYPE, NULL);
+
+    AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+    priv->account = account;
+
+    build_tab_view(ACCOUNT_DEVICES_TAB(view));
+
+    return (GtkWidget *)view;
+}
diff --git a/src/accountdevicestab.h b/src/accountdevicestab.h
new file mode 100644
index 0000000..79b9209
--- /dev/null
+++ b/src/accountdevicestab.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+class Account;
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_DEVICES_TAB_TYPE            (account_devices_tab_get_type ())
+#define ACCOUNT_DEVICES_TAB(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTab))
+#define ACCOUNT_DEVICES_TAB_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabClass))
+#define IS_ACCOUNT_DEVICES_TAB(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_DEVICES_TAB_TYPE))
+#define IS_ACCOUNT_DEVICES_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_DEVICES_TAB_TYPE))
+
+typedef struct _AccountDevicesTab      AccountDevicesTab;
+typedef struct _AccountDevicesTabClass AccountDevicesTabClass;
+
+GType      account_devices_tab_get_type      (void) G_GNUC_CONST;
+GtkWidget *account_devices_tab_new           (Account *account);
+
+G_END_DECLS
diff --git a/src/accountmigrationview.cpp b/src/accountmigrationview.cpp
new file mode 100644
index 0000000..bd0428e
--- /dev/null
+++ b/src/accountmigrationview.cpp
@@ -0,0 +1,282 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+
+// GTK+ related
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+// LRC
+#include <codecmodel.h>
+#include <account.h>
+#include <accountmodel.h>
+#include <profilemodel.h>
+#include <profile.h>
+#include <personmodel.h>
+#include <globalinstances.h>
+
+// Ring Client
+#include "avatarmanipulation.h"
+#include "accountmigrationview.h"
+#include "utils/models.h"
+#include "native/pixbufmanipulator.h"
+#include "video/video_widget.h"
+
+/* size of avatar */
+static constexpr int AVATAR_WIDTH  = 100; /* px */
+static constexpr int AVATAR_HEIGHT = 100; /* px */
+
+struct _AccountMigrationView
+{
+    GtkBox parent;
+};
+
+struct _AccountMigrationViewClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _AccountMigrationViewPrivate AccountMigrationViewPrivate;
+
+struct _AccountMigrationViewPrivate
+{
+    Account   *account;
+    GtkWidget *stack_account_migration;
+    QMetaObject::Connection state_changed;
+    guint timeout_tag;
+
+    /* generating_account_view */
+    GtkWidget *migrating_account_view;
+
+    /* error_view */
+    GtkWidget *error_view;
+    GtkWidget *button_error_view_ok;
+
+    /* main_view */
+    GtkWidget *main_view;
+    GtkWidget *label_account_alias;
+    GtkWidget *label_account_id;
+    GtkWidget *image_avatar;
+    GtkWidget *label_password_error;
+    GtkWidget *entry_password;
+    GtkWidget *entry_password_confirm;
+    GtkWidget *button_migrate_account;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountMigrationView, account_migration_view, GTK_TYPE_BOX);
+
+#define ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewPrivate))
+
+/* signals */
+enum {
+    ACCOUNT_MIGRATION_COMPLETED,
+    ACCOUNT_MIGRATION_FAILED,
+    LAST_SIGNAL
+};
+
+static guint account_migration_view_signals[LAST_SIGNAL] = { 0 };
+
+static void
+account_migration_view_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(account_migration_view_parent_class)->dispose(object);
+}
+
+static void
+account_migration_view_init(AccountMigrationView *view)
+{
+    gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_migration_view_class_init(AccountMigrationViewClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = account_migration_view_dispose;
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+                                                "/cx/ring/RingGnome/accountmigrationview.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, stack_account_migration);
+
+    /* error_view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, error_view);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_error_view_ok);
+
+    /* migrating_account_view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, migrating_account_view);
+
+    /* main_view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, main_view);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_alias);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_id);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, image_avatar);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_password_error);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password_confirm);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_migrate_account);
+
+    /* add signals */
+    account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED] = g_signal_new("account-migration-completed",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+    account_migration_view_signals[ACCOUNT_MIGRATION_FAILED] = g_signal_new("account-migration-failed",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+}
+
+static gboolean
+migration_timeout(AccountMigrationView *view)
+{
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+    QObject::disconnect(priv->state_changed);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->error_view);
+    return G_SOURCE_REMOVE;
+}
+
+static void
+button_error_view_ok_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
+{
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+    g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_FAILED], 0);
+}
+
+static void
+migrate_account_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
+{
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+    gtk_widget_hide(priv->label_password_error);
+
+    /* Check for correct password */
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (g_strcmp0(password, password_confirm) != 0)
+    {
+        gtk_widget_show(priv->label_password_error);
+        return;
+    }
+    else
+    {
+        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->migrating_account_view);
+
+        priv->account->setArchivePassword(password);
+        priv->account->performAction(Account::EditAction::SAVE);
+
+        // Timeout in 30 seconds
+        priv->timeout_tag = g_timeout_add_full(G_PRIORITY_DEFAULT, 30000, (GSourceFunc)migration_timeout, view, NULL);
+
+        priv->state_changed = QObject::connect(
+            priv->account,
+            &Account::stateChanged,
+            [=] (Account::RegistrationState state) {
+                switch(state)
+                {
+                    case Account::RegistrationState::READY:
+                    case Account::RegistrationState::TRYING:
+                    case Account::RegistrationState::UNREGISTERED:
+                    {
+                        // Make sure that the account is ready to be displayed.
+                        priv->account << Account::EditAction::RELOAD;
+
+                        AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+                        QObject::disconnect(priv->state_changed); // only want to emit once
+                        g_source_remove(priv->timeout_tag); // We didn't timeout
+                        g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0);
+                        break;
+                    }
+                    case Account::RegistrationState::ERROR:
+                    case Account::RegistrationState::INITIALIZING:
+                    case Account::RegistrationState::COUNT__:
+                    {
+                        // Keep waiting...
+                        break;
+                    }
+                }
+            }
+        );
+
+    }
+}
+
+static void
+password_entry_changed(G_GNUC_UNUSED GtkEntry* entry, AccountMigrationView *view)
+{
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (strlen(password) > 0 && strlen(password_confirm) > 0)
+    {
+        gtk_widget_set_sensitive(priv->button_migrate_account, TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(priv->button_migrate_account, FALSE);
+    }
+}
+
+static void
+build_migration_view(AccountMigrationView *view)
+{
+    g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view));
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+
+    g_signal_connect(priv->button_migrate_account, "clicked", G_CALLBACK(migrate_account_clicked), view);
+    g_signal_connect(priv->button_error_view_ok, "clicked", G_CALLBACK(button_error_view_ok_clicked), view);
+    g_signal_connect(priv->entry_password, "changed", G_CALLBACK(password_entry_changed), view);
+    g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(password_entry_changed), view);
+
+    gtk_label_set_text(GTK_LABEL(priv->label_account_alias), priv->account->alias().toUtf8().constData());
+    gtk_label_set_text(GTK_LABEL(priv->label_account_id), priv->account->id().constData());
+
+    /* set the avatar picture */
+    auto photo = GlobalInstances::pixmapManipulator().contactPhoto(
+                    ProfileModel::instance().selectedProfile()->person(),
+                    QSize(AVATAR_WIDTH, AVATAR_HEIGHT),
+                    false);
+    std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>();
+
+    if (photo.isValid()) {
+        gtk_image_set_from_pixbuf (GTK_IMAGE(priv->image_avatar),  pixbuf_photo.get());
+    } else {
+        g_warning("invalid pixbuf");
+    }
+}
+
+GtkWidget *
+account_migration_view_new(Account* account)
+{
+    gpointer view = g_object_new(ACCOUNT_MIGRATION_VIEW_TYPE, NULL);
+    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+    priv->account = account;
+    build_migration_view(ACCOUNT_MIGRATION_VIEW(view));
+    return (GtkWidget *)view;
+}
diff --git a/src/accountmigrationview.h b/src/accountmigrationview.h
new file mode 100644
index 0000000..376bd4c
--- /dev/null
+++ b/src/accountmigrationview.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+class Account;
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_MIGRATION_VIEW_TYPE            (account_migration_view_get_type ())
+#define ACCOUNT_MIGRATION_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationView))
+#define ACCOUNT_MIGRATION_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewClass))
+#define IS_ACCOUNT_MIGRATION_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_MIGRATION_VIEW_TYPE))
+#define IS_ACCOUNT_MIGRATION_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_MIGRATION_VIEW_TYPE))
+
+typedef struct _AccountMigrationView      AccountMigrationView;
+typedef struct _AccountMigrationViewClass AccountMigrationViewClass;
+
+GType      account_migration_view_get_type      (void) G_GNUC_CONST;
+GtkWidget *account_migration_view_new           (Account *account);
+
+G_END_DECLS
diff --git a/src/accountview.cpp b/src/accountview.cpp
index 9ba34f1..8322465 100644
--- a/src/accountview.cpp
+++ b/src/accountview.cpp
@@ -29,15 +29,19 @@
 #include "models/gtkqtreemodel.h"
 #include "models/activeitemproxymodel.h"
 #include "accountgeneraltab.h"
+#include "accountcreationwizard.h"
 #include "accountaudiotab.h"
 #include "accountvideotab.h"
 #include "accountadvancedtab.h"
 #include "accountsecuritytab.h"
+#include "accountdevicestab.h"
 #include "dialogs.h"
 #include <glib/gprintf.h>
 #include "utils/models.h"
 #include "accountimportexportview.h"
 
+static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
+
 struct _AccountView
 {
     GtkPaned parent;
@@ -131,6 +135,16 @@
     AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
 
     auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+    if(IS_ACCOUNT_CREATION_WIZARD(old_view))
+    {
+        /* When using the account creation wizard, The user might create
+         * accounts that will be deleted by the daemon. The fact that the
+         * selection has changed should be ignored. The account creation wizard
+         * should be responsible to remove itself of the stack and then call
+         * account_selection_changed.
+         */
+        return;
+    }
 
     QModelIndex account_idx = get_index_from_selection(selection);
     if (!account_idx.isValid()) {
@@ -176,6 +190,10 @@
         gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  security_tab,
                                  gtk_label_new(C_("Account settings", "Security")));
+        auto devices_tab = create_scrolled_account_view(account_devices_tab_new(account));
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
+                                 devices_tab,
+                                 gtk_label_new(C_("Account settings", "Devices")));
 
         gtk_widget_show_all(hbox_account);
         /* set the tab displayed to the same as the prev account selected */
@@ -296,6 +314,54 @@
 }
 
 static void
+hide_account_creation_wizard(AccountView *view)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(view));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+    if (IS_ACCOUNT_CREATION_WIZARD(old_view))
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+    }
+
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+    account_selection_changed(selection, view);
+}
+
+static void
+show_account_creation_wizard(AccountView *view)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(view));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+    auto account_creation_wizard = gtk_stack_get_child_by_name(
+        GTK_STACK(priv->stack_account),
+        ACCOUNT_CREATION_WIZARD_VIEW_NAME
+    );
+
+    if (!account_creation_wizard)
+    {
+        account_creation_wizard = account_creation_wizard_new(true);
+        gtk_stack_add_named(GTK_STACK(priv->stack_account),
+                            account_creation_wizard,
+                            ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+        g_signal_connect_swapped(account_creation_wizard, "account-creation-completed", G_CALLBACK(hide_account_creation_wizard), view);
+        g_signal_connect_swapped(account_creation_wizard, "account-creation-canceled", G_CALLBACK(hide_account_creation_wizard), view);
+    }
+
+    gtk_widget_show(account_creation_wizard);
+
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_account), ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+
+    /* remove the old view */
+    if (old_view)
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+}
+
+static void
 add_account(G_GNUC_UNUSED GtkWidget *entry, AccountView *view)
 {
     g_return_if_fail(IS_ACCOUNT_VIEW(view));
@@ -311,19 +377,26 @@
         if (protocol_idx.isValid()) {
             protocol_idx = priv->active_protocols->mapToSource(protocol_idx);
 
-            /* show working dialog in case save operation takes time */
-            GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
-            gtk_window_present(GTK_WINDOW(working));
+            Account::Protocol protocol = qvariant_cast<Account::Protocol>(protocol_idx.data((int)ProtocolModel::Role::Protocol));
+            if (protocol == Account::Protocol::RING)
+            {
+                show_account_creation_wizard(view);
+            }
+            else
+            {
+                /* show working dialog in case save operation takes time */
+                GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
+                gtk_window_present(GTK_WINDOW(working));
 
-            auto account = AccountModel::instance().add(QString(_("New Account")), protocol_idx);
-            if (account->protocol() == Account::Protocol::RING)
-                account->setDisplayName(_("New Account"));
+                AccountModel::instance().add(QString(_("New Account")), protocol_idx);
 
-            /* now save after a short timeout to make sure that
-             * the save doesn't happen before the "working" dialog is presented
-             * the timeout function should destroy the "working" dialog when done saving
-             */
-            g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+                /* now save after a short timeout to make sure that
+                 * the save doesn't happen before the "working" dialog is presented
+                 * the timeout function should destroy the "working" dialog when done saving
+                 */
+                g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+            }
+
         }
     }
 }
@@ -365,6 +438,7 @@
                     display_state = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", escaped_state);
                 break;
                 case Account::RegistrationState::TRYING:
+                case Account::RegistrationState::INITIALIZING:
                     display_state = g_strdup_printf("<span fgcolor=\"orange\">%s</span>", escaped_state);
                 break;
                 case Account::RegistrationState::ERROR:
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index 95d86d3..15cd5a3 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -64,13 +64,16 @@
 #include "generalsettingsview.h"
 #include "utils/accounts.h"
 #include "ringwelcomeview.h"
+#include "accountmigrationview.h"
+#include "accountcreationwizard.h"
 #include "recentcontactsview.h"
 #include "chatview.h"
 #include "avatarmanipulation.h"
 #include "utils/files.h"
 
 static constexpr const char* CALL_VIEW_NAME             = "calls";
-static constexpr const char* CREATE_ACCOUNT_VIEW_NAME   = "wizard";
+static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
+static constexpr const char* ACCOUNT_MIGRATION_VIEW_NAME       = "account-migration-view";
 static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
 static constexpr const char* AUDIO_SETTINGS_VIEW_NAME   = "audio";
 static constexpr const char* MEDIA_SETTINGS_VIEW_NAME   = "media";
@@ -125,21 +128,6 @@
 
     gboolean   show_settings;
 
-    /* account creation */
-    GtkWidget *account_creation;
-    GtkWidget *image_ring_logo;
-    GtkWidget *vbox_account_creation_entry;
-    GtkWidget *entry_alias;
-    GtkWidget *label_default_name;
-    GtkWidget *label_paceholder;
-    GtkWidget *label_generating_account;
-    GtkWidget *spinner_generating_account;
-    GtkWidget *button_account_creation_next;
-    GtkWidget *box_avatarselection;
-    GtkWidget* avatar_manipulation;
-
-    QMetaObject::Connection hash_updated;
-
     /* allocd qmodels */
     NumberCompletionModel *q_completion_model;
 
@@ -591,153 +579,56 @@
     }
 }
 
-static gboolean
-create_ring_account(RingMainWindow *win)
-{
-    g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
-    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
-
-    /* create account and set UPnP enabled, as its not by default in the daemon */
-    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
-    Account *account = nullptr;
-
-    /* get profile (if so) */
-    auto profile = ProfileModel::instance().selectedProfile();
-
-    if (profile) {
-        if (alias && strlen(alias) > 0) {
-            account = AccountModel::instance().add(alias, Account::Protocol::RING);
-            profile->person()->setFormattedName(alias);
-        } else {
-            auto unknown_alias = C_("The default username / account alias, if none is set by the user", "Unknown");
-            account = AccountModel::instance().add(unknown_alias, Account::Protocol::RING);
-            profile->person()->setFormattedName(unknown_alias);
-        }
-    }
-
-    account->setDisplayName(alias); // set the display name to the same as the alias
-    account->setUpnpEnabled(TRUE);
-
-    /* wait for hash to be generated to show the main view */
-    priv->hash_updated = QObject::connect(
-        account,
-        &Account::changed,
-        [=] (Account *a) {
-            QString hash = a->username();
-            if (!hash.isEmpty()) {
-                /* show the call view */
-                gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
-                gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
-
-                /* show the settings button*/
-                gtk_widget_show(priv->ring_settings);
-
-                //once the account is created we don't want this lambda to be called again
-                QObject::disconnect(priv->hash_updated);
-            }
-        }
-    );
-
-    account->performAction(Account::EditAction::SAVE);
-    profile->save();
-
-    return G_SOURCE_REMOVE;
-}
-
 static void
-alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
+on_account_creation_completed(RingMainWindow *win)
 {
     g_return_if_fail(IS_RING_MAIN_WINDOW(win));
     RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
 
-    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
-    if (!alias || strlen(alias) == 0) {
-        gtk_widget_show(priv->label_default_name);
-        gtk_widget_hide(priv->label_paceholder);
-    } else {
-        gtk_widget_hide(priv->label_default_name);
-        gtk_widget_show(priv->label_paceholder);
+    gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
+
+    /* destroy the wizard */
+    GtkWidget* account_creation_wizard = gtk_stack_get_child_by_name(
+        GTK_STACK(priv->stack_main_view),
+        ACCOUNT_CREATION_WIZARD_VIEW_NAME
+    );
+    if (account_creation_wizard)
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), account_creation_wizard);
+        gtk_widget_destroy(account_creation_wizard);
     }
+
+    /* show the settings button*/
+    gtk_widget_show(priv->ring_settings);
 }
 
 static void
-account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
+show_account_creation_wizard(RingMainWindow *win)
 {
     RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
 
-    /* show/hide relevant widgets */
-    gtk_widget_hide(priv->vbox_account_creation_entry);
-    gtk_widget_hide(priv->button_account_creation_next);
-    gtk_widget_show(priv->label_generating_account);
-    gtk_widget_show(priv->spinner_generating_account);
+    auto account_creation_wizard = gtk_stack_get_child_by_name(
+        GTK_STACK(priv->stack_main_view),
+        ACCOUNT_CREATION_WIZARD_VIEW_NAME
+    );
 
-    /* make sure the AvatarManipulation widget is destroyed so the VideoWidget inside of it is too;
-     * NOTE: destorying its parent (box_avatarselection) first will cause a mystery 'BadDrawable'
-     * crash due to X error */
-    gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
-    priv->avatar_manipulation = nullptr;
+    if (!account_creation_wizard)
+    {
+        account_creation_wizard = account_creation_wizard_new(false);
+        g_signal_connect_swapped(account_creation_wizard, "account-creation-completed", G_CALLBACK(on_account_creation_completed), win);
 
-    /* now create account after a short timeout so that the the save doesn't
-     * happen freeze the client before the widget changes happen;
-     * the timeout function should then display the next step in account creation
-     */
-    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_ring_account, win, NULL);
-}
-
-static void
-entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
-{
-    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
-
-    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
-    if (strlen(alias) > 0)
-        gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
-}
-
-static void
-show_account_creation(RingMainWindow *win)
-{
-    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
-
-    gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
-                        priv->account_creation,
-                        CREATE_ACCOUNT_VIEW_NAME);
+        gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
+                            account_creation_wizard,
+                            ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+    }
 
     /* hide settings button until account creation is complete */
     gtk_widget_hide(priv->ring_settings);
 
-    /* set ring logo */
-    GError *error = NULL;
-    GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
-                                                                  -1, 50, TRUE, &error);
-    if (logo_ring == NULL) {
-        g_debug("Could not load logo: %s", error->message);
-        g_clear_error(&error);
-    } else
-        gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_ring_logo), logo_ring);
+    gtk_widget_show(account_creation_wizard);
 
-    /* use the real name / username of the logged in user as the default */
-    const char* real_name = g_get_real_name();
-    const char* user_name = g_get_user_name();
-    g_debug("real_name = %s",real_name);
-    g_debug("user_name = %s",user_name);
-
-    /* check first if the real name was determined */
-    if (g_strcmp0 (real_name,"Unknown") != 0)
-        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), real_name);
-    else
-        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), user_name);
-
-    /* avatar manipulation widget */
-    priv->avatar_manipulation = avatar_manipulation_new_from_wizard();
-    gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
-
-    /* connect signals */
-    g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
-    g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
-    g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
-
-    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_VIEW_NAME);
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_CREATION_WIZARD_VIEW_NAME);
 }
 
 static void
@@ -1121,6 +1012,46 @@
 }
 
 static void
+handle_account_migrations(RingMainWindow *win)
+{
+    RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+    /* If there is an existing migration view, remove it */
+    GtkWidget* old_view = gtk_stack_get_child_by_name(GTK_STACK(priv->stack_main_view), ACCOUNT_MIGRATION_VIEW_NAME);
+    if (old_view)
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), old_view);
+    }
+
+    QList<Account*> accounts = AccountModel::instance().accountsToMigrate();
+    if (!accounts.isEmpty())
+    {
+        Account* account = accounts.first();
+        g_debug("Migrating account: %s", account->id().toStdString().c_str());
+
+        GtkWidget* account_migration_view = account_migration_view_new(account);
+        g_signal_connect_swapped(account_migration_view, "account-migration-completed", G_CALLBACK(handle_account_migrations), win);
+        g_signal_connect_swapped(account_migration_view, "account-migration-failed", G_CALLBACK(handle_account_migrations), win);
+
+        gtk_widget_hide(priv->ring_settings);
+        gtk_widget_show(account_migration_view);
+        gtk_stack_add_named(
+            GTK_STACK(priv->stack_main_view),
+            account_migration_view,
+            ACCOUNT_MIGRATION_VIEW_NAME
+        );
+        gtk_stack_set_visible_child_name(
+            GTK_STACK(priv->stack_main_view),
+            ACCOUNT_MIGRATION_VIEW_NAME
+        );
+    }
+    else
+    {
+        gtk_widget_show(priv->ring_settings);
+    }
+}
+
+static void
 ring_main_window_init(RingMainWindow *win)
 {
     RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
@@ -1177,7 +1108,7 @@
         gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
     } else {
         /* user has to create the ring account */
-        show_account_creation(win);
+        show_account_creation_wizard(win);
     }
 
     /* init the settings views */
@@ -1322,6 +1253,8 @@
     /* set the search entry placeholder text */
     gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
                                    C_("Please try to make the translation 50 chars or less so that it fits into the layout", "Search contacts or enter number"));
+
+    handle_account_migrations(win);
 }
 
 static void
@@ -1374,18 +1307,6 @@
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
-
-    /* account creation */
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_account_creation_entry);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_default_name);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_paceholder);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, box_avatarselection);
 }
 
 GtkWidget *