Commit fcb4ca63 authored by Loïc Siret's avatar Loïc Siret

multi-device: update Ring account creation wizard

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.

Change-Id: I6840d968e52de014ab0aa9dd6f5bc1ab31f9e9ec
Tuleap: #958
parent 624b1a8e
......@@ -146,6 +146,12 @@ SET(ringclient_CONTROLLERS
src/ExportPasswordWC.h
src/MigrateRingAccountsWC.mm
src/MigrateRingAccountsWC.h
src/RingWizardChooseVC.h
src/RingWizardChooseVC.mm
src/RingWizardLinkAccountVC.h
src/RingWizardLinkAccountVC.mm
src/RingWizardNewAccountVC.mm
src/RingWizardNewAccountVC.h
)
SET(ringclient_BACKENDS
......@@ -207,7 +213,10 @@ SET(ringclient_XIBS
PersonLinker
Broker
Conversation
AccDevices)
AccDevices
RingWizardLinkAccount
RingWizardNewAccount
RingWizardChoose)
# Icons
# This part tells CMake where to find and install the file itself
......
......@@ -38,6 +38,7 @@
#import "AccRingVC.h"
#import "AccDevicesVC.h"
#import "PathPasswordWC.h"
#import "RingWizardWC.h"
@interface AccountsVC () <PathPasswordDelegate>
......@@ -63,6 +64,7 @@
@property AccAdvancedVC* advancedVC;
@property AccSecurityVC* securityVC;
@property PathPasswordWC* passwordWC;
@property RingWizardWC* wizard;
@end
......@@ -79,6 +81,7 @@
@synthesize accountDetailsView;
@synthesize treeController;
@synthesize passwordWC;
@synthesize wizard;
NSInteger const TAG_CHECK = 100;
NSInteger const TAG_NAME = 200;
......@@ -164,18 +167,52 @@ NSInteger const TAG_TYPE = 400;
[self.ringDevicesTabItem setView:self.devicesVC.view];
}
- (IBAction)addAccount:(id)sender {
- (void)createSIPAccount
{
QModelIndex qIdx = AccountModel::instance().protocolModel()->selectionModel()->currentIndex();
auto newAccName = [[NSString alloc] initWithFormat:@"%@ account",
AccountModel::instance().protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString(), nil];
AccountModel::instance().protocolModel()->data(qIdx, Qt::DisplayRole).toString().toNSString(), nil];
auto acc = AccountModel::instance().add([newAccName UTF8String], qIdx);
acc->setDisplayName(acc->alias());
AccountModel::instance().save();
}
- (IBAction)protocolSelectedChanged:(id)sender {
- (void)createRingAccount
{
wizard = [[RingWizardWC alloc] initWithWindowNibName:@"RingWizard"];
[wizard showChooseWithCancelButton: YES];
// [wizard.window makeKeyAndOrderFront:self];
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
[self.view.window beginSheet:wizard.window completionHandler:nil];
#else
[NSApp beginSheet: wizard.window
modalForWindow: self.view.window
modalDelegate: self
didEndSelector: nil
contextInfo: nil];
#endif
[wizard showWindow:self];
}
- (IBAction)addAccount:(id)sender
{
QModelIndex qProtocolIdx = AccountModel::instance().protocolModel()->selectionModel()->currentIndex();
auto protocol = qvariant_cast<Account::Protocol> (AccountModel::instance().protocolModel()->data( qProtocolIdx, Qt::UserRole));
switch (protocol){
case Account::Protocol::SIP:{
[self createSIPAccount];
break;
}
case Account::Protocol::RING:{
[self createRingAccount];
break;
}
}
}
- (IBAction)protocolSelectedChanged:(id)sender
{
int index = [sender indexOfSelectedItem];
auto qIdx = AccountModel::instance().protocolModel()->index(index, 0);
AccountModel::instance().protocolModel()->selectionModel()->setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
......@@ -187,7 +224,6 @@ NSInteger const TAG_TYPE = 400;
for(NSTabViewItem* item in configPanels.tabViewItems) {
[configPanels removeTabViewItem:item];
}
[configPanels insertTabViewItem:generalTabItem atIndex:0];
[configPanels insertTabViewItem:mediaTabItem atIndex:1];
[configPanels insertTabViewItem:advancedTabItem atIndex:2];
......
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import <Cocoa/Cocoa.h>
@class RingWizardWC;
typedef NS_ENUM(NSInteger, WizardAction) {
WIZARD_ACTION_INVALID = -1,
WIZARD_ACTION_NEW = 0,
WIZARD_ACTION_LINK = 1,
};
@protocol RingWizardChooseDelegate <NSObject>
- (void)didCompleteWithAction:(WizardAction)action;
@end
@interface RingWizardChooseVC : NSViewController
@property (weak, nonatomic) id <RingWizardChooseDelegate> delegate;
@property BOOL isCancelable;
- (void)showCancelButton:(BOOL)showCancel;
@end
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import "RingWizardChooseVC.h"
@interface RingWizardChooseVC()
@end
@implementation RingWizardChooseVC
@synthesize delegate;
- (void)showCancelButton:(BOOL)showCancel{
self.isCancelable = showCancel;
}
- (IBAction)createRingAccount:(id)sender
{
if ([self.delegate respondsToSelector:@selector(didCompleteWithAction:)]){
[delegate didCompleteWithAction:WizardAction::WIZARD_ACTION_NEW];
}
}
- (IBAction)linkExistingRingAccount:(id)sender
{
if ([self.delegate respondsToSelector:@selector(didCompleteWithAction:)]){
[delegate didCompleteWithAction:WizardAction::WIZARD_ACTION_LINK];
}
}
- (IBAction)onCancel:(id)sender
{
if ([self.delegate respondsToSelector:@selector(didCompleteWithAction:)]){
[delegate didCompleteWithAction:WIZARD_ACTION_INVALID];
}
}
@end
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import <Cocoa/Cocoa.h>
@protocol RingWizardLinkDelegate <NSObject>
- (void)didLinkAccountWithSuccess:(BOOL)success;
@end
@interface RingWizardLinkAccountVC : NSViewController
@property (nonatomic, weak) NSWindowController <RingWizardLinkDelegate>* delegate;
@property (nonatomic, weak) NSString* pinValue;
@property (nonatomic, weak) NSString* passwordValue;
- (void)show;
@end
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import "RingWizardLinkAccountVC.h"
//Cocoa
#import <AddressBook/AddressBook.h>
#import <Quartz/Quartz.h>
//Qt
#import <QUrl>
#import <QPixmap>
//LRC
#import <accountmodel.h>
#import <protocolmodel.h>
#import <profilemodel.h>
#import <QItemSelectionModel>
#import <account.h>
#import <certificate.h>
#import <profilemodel.h>
#import <profile.h>
#import <person.h>
#import "Constants.h"
@interface RingWizardLinkAccountVC ()
@end
@implementation RingWizardLinkAccountVC {
__unsafe_unretained IBOutlet NSView* initialContainer;
__unsafe_unretained IBOutlet NSTextField* pinField;
__unsafe_unretained IBOutlet NSSecureTextField* passwordField;
__unsafe_unretained IBOutlet NSTextField* pinLabel;
__unsafe_unretained IBOutlet NSTextField* passwordLabel;
__unsafe_unretained IBOutlet NSButton* createButton;
__unsafe_unretained IBOutlet NSView* loadingContainer;
__unsafe_unretained IBOutlet NSProgressIndicator* progressBar;
__unsafe_unretained IBOutlet NSView* errorContainer;
Account* accountToCreate;
NSTimer* errorTimer;
QMetaObject::Connection stateChanged;
}
- (void)show
{
[initialContainer setHidden:NO];
[loadingContainer setHidden:YES];
[errorContainer setHidden:YES];
}
- (void)showError
{
[initialContainer setHidden:YES];
[loadingContainer setHidden:YES];
[errorContainer setHidden:NO];
}
- (void)showLoading
{
[initialContainer setHidden:YES];
[loadingContainer setHidden:NO];
[progressBar startAnimation:nil];
[errorContainer setHidden:YES];
}
- (IBAction)importRingAccount:(id)sender
{
[self showLoading];
if (auto profile = ProfileModel::instance().selectedProfile()) {
profile->person()->setFormattedName([NSFullUserName() UTF8String]);
profile->save();
}
accountToCreate = AccountModel::instance().add(QString::fromNSString(NSFullUserName()), Account::Protocol::RING);
accountToCreate->setArchivePin(QString::fromNSString(self.pinValue));
accountToCreate->setArchivePassword(QString::fromNSString(self.passwordValue));
[self setCallback];
[self performSelector:@selector(saveAccount) withObject:nil afterDelay:1];
[self registerDefaultPreferences];
}
- (IBAction)dismissViewWithError:(id)sender
{
[self.delegate didLinkAccountWithSuccess:NO];
}
- (IBAction)back:(id)sender
{
[self show];
}
/**
* Set default values for preferences
*/
- (void)registerDefaultPreferences
{
// enable AutoStartup
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItemsRef == nil) return;
CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
if (itemRef) CFRelease(itemRef);
// enable Notifications
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:Preferences::Notifications];
}
- (void)saveAccount
{
accountToCreate->setUpnpEnabled(YES); // Always active upnp
accountToCreate << Account::EditAction::SAVE;
}
- (void)setCallback
{
errorTimer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(didLinkFailed) userInfo:nil
repeats:NO];
stateChanged = QObject::connect(&AccountModel::instance(),
&AccountModel::accountStateChanged,
[=](Account *account, const Account::RegistrationState state) {
switch(state){
case Account::RegistrationState::READY:
case Account::RegistrationState::TRYING:
case Account::RegistrationState::UNREGISTERED:{
accountToCreate<< Account::EditAction::RELOAD;
QObject::disconnect(stateChanged);
[errorTimer invalidate];
[self.delegate didLinkAccountWithSuccess:YES];
break;
}
case Account::RegistrationState::ERROR:
QObject::disconnect(stateChanged);
[errorTimer invalidate];
[self showError];
break;
case Account::RegistrationState::INITIALIZING:
case Account::RegistrationState::COUNT__:{
//DO Nothing
break;
}
}
});
}
- (void)didLinkFailed
{
[self showError];
}
@end
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import <Cocoa/Cocoa.h>
@protocol RingWizardNewDelegate <NSObject>
- (void)didCreateAccountWithSuccess:(BOOL)success;
@end
@interface RingWizardNewAccountVC : NSViewController
@property (nonatomic, weak)NSWindowController <RingWizardNewDelegate>* delegate;
@property (nonatomic, weak)NSString* alias;
@property (nonatomic, weak)NSString* password;
- (void)show;
@end
/*
* Copyright (C) 2015-2016 Savoir-faire Linux Inc.
* Author: Loïc Siret <loic.siret@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.
*/
#import "RingWizardNewAccountVC.h"
//Cocoa
#import <AddressBook/AddressBook.h>
#import <Quartz/Quartz.h>
//Qt
#import <QUrl>
#import <QPixmap>
//LRC
#import <accountmodel.h>
#import <protocolmodel.h>
#import <profilemodel.h>
#import <QItemSelectionModel>
#import <account.h>
#import <certificate.h>
#import <profilemodel.h>
#import <profile.h>
#import <person.h>
#import "AppDelegate.h"
#import "Constants.h"
#import "views/NSImage+Extensions.h"
#import "views/NSColor+RingTheme.h"
@interface RingWizardNewAccountVC ()
@end
@implementation RingWizardNewAccountVC
{
__unsafe_unretained IBOutlet NSButton* photoView;
__unsafe_unretained IBOutlet NSTextField* nicknameField;
__unsafe_unretained IBOutlet NSSecureTextField* passwordField;
__unsafe_unretained IBOutlet NSProgressIndicator* progressBar;
__unsafe_unretained IBOutlet NSTextField* indicationLabel;
__unsafe_unretained IBOutlet NSTextField* passwordLabel;
__unsafe_unretained IBOutlet NSButton* createButton;
__unsafe_unretained IBOutlet NSButton* cancelButton;
Account* accountToCreate;
NSTimer* errorTimer;
QMetaObject::Connection stateChanged;
}
NSInteger const NICKNAME_TAG = 1;
- (void)show
{
AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[nicknameField setTag:NICKNAME_TAG];
[nicknameField setStringValue:NSFullUserName()];
[self controlTextDidChange:[NSNotification notificationWithName:@"PlaceHolder" object:nicknameField]];
NSData* imgData = [[[ABAddressBook sharedAddressBook] me] imageData];
if (imgData != nil) {
[photoView setImage:[[NSImage alloc] initWithData:imgData]];
} else
[photoView setImage:[NSImage imageNamed:@"default_user_icon"]];
[photoView setWantsLayer: YES];
photoView.layer.cornerRadius = photoView.frame.size.width / 2;
photoView.layer.masksToBounds = YES;
}
- (IBAction)editPhoto:(id)sender
{
auto pictureTaker = [IKPictureTaker pictureTaker];
[pictureTaker beginPictureTakerSheetForWindow:[self.delegate window]
withDelegate:self
didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}
- (void)pictureTakerDidEnd:(IKPictureTaker *) picker
returnCode:(NSInteger) code
contextInfo:(void*) contextInfo
{
if (auto outputImage = [picker outputImage]) {
[photoView setImage:outputImage];
} else
[photoView setImage:[NSImage imageNamed:@"default_user_icon"]];
}
- (IBAction)createRingAccount:(id)sender
{
[nicknameField setHidden:YES];
[progressBar setHidden:NO];
[createButton setHidden:YES];
[photoView setHidden:YES];
[passwordField setHidden:YES];
[passwordLabel setHidden:YES];
[cancelButton setHidden:YES];
[progressBar startAnimation:nil];
[indicationLabel setStringValue:NSLocalizedString(@"Just a moment...",
@"Indication for user")];
if ([self.alias isEqualToString:@""]) {
self.alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
}
accountToCreate = AccountModel::instance().add(QString::fromNSString(self.alias), Account::Protocol::RING);
accountToCreate->setAlias([self.alias UTF8String]);
accountToCreate->setDisplayName([self.alias UTF8String]);
if (auto profile = ProfileModel::instance().selectedProfile()) {
profile->person()->setFormattedName([self.alias UTF8String]);
QPixmap p;
auto smallImg = [NSImage imageResize:[photoView image] newSize:{100,100}];
if (p.loadFromData(QByteArray::fromNSData([smallImg TIFFRepresentation]))) {
profile->person()->setPhoto(QVariant(p));
}
profile->save();
}
QModelIndex qIdx = AccountModel::instance().protocolModel()->selectionModel()->currentIndex();
[self setCallback];
[self performSelector:@selector(saveAccount) withObject:nil afterDelay:1];
[self registerDefaultPreferences];
}
/**
* Set default values for preferences
*/
- (void)registerDefaultPreferences
{
// enable AutoStartup
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItemsRef == nil) return;
CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
if (itemRef) CFRelease(itemRef);
// enable Notifications
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:Preferences::Notifications];
}
- (void)saveAccount
{
accountToCreate->setArchivePassword(QString::fromNSString(passwordField.stringValue));
accountToCreate->setUpnpEnabled(YES); // Always active upnp
accountToCreate << Account::EditAction::SAVE;
}
- (void)setCallback
{
errorTimer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(didCreateFailed) userInfo:nil
repeats:NO];
stateChanged = QObject::connect(&AccountModel::instance(),
&AccountModel::accountStateChanged,
[=](Account *account, const Account::RegistrationState state) {
switch(state){
case Account::RegistrationState::READY:
case Account::RegistrationState::TRYING:
case Account::RegistrationState::UNREGISTERED:{
accountToCreate<< Account::EditAction::RELOAD;
QObject::disconnect(stateChanged);
[self.delegate didCreateAccountWithSuccess:YES];
break;
}
case Account::RegistrationState::ERROR:
QObject::disconnect(stateChanged);
[self.delegate didCreateAccountWithSuccess:NO];
break;
case Account::RegistrationState::INITIALIZING:
case Account::RegistrationState::COUNT__:{
//DO Nothing
break;
}
}
});
}
- (void)didCreateFailed
{
[self.delegate didCreateAccountWithSuccess:NO];
}
- (IBAction)cancel:(id)sender
{
[self.delegate didCreateAccountWithSuccess:NO];
}
#pragma mark - NSOpenSavePanelDelegate delegate methods
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError
{
return YES;
}
-(void)controlTextDidChange:(NSNotification *)notif
{
NSTextField* textField = [notif object];
// else it is NICKNAME_TAG field
NSString* alias = textField.stringValue;
if ([alias isEqualToString:@""]) {
alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
}
self.alias = alias;
}
@end
......@@ -18,7 +18,15 @@
*/
#import <Cocoa/Cocoa.h>
#import "RingWizardChooseVC.h"
#import "RingWizardNewAccountVC.h"
#import "RingWizardLinkAccountVC.h"
@interface RingWizardWC : NSWindowController <NSWindowDelegate, NSPathControlDelegate,
NSOpenSavePanelDelegate, RingWizardChooseDelegate, RingWizardNewDelegate,
RingWizardLinkDelegate>
@interface RingWizardWC : NSWindowController <NSWindowDelegate, NSPathControlDelegate, NSOpenSavePanelDelegate>
- (void)showChooseWithCancelButton:(BOOL)showCancel;
- (void)showNewAccountVC;
- (void)showLinkAccountVC;
@end
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="RingWizardChooseVC">
<connections>
<outlet property="view" destination="kwg-fS-3aI" id="C8q-qb-geg"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="kwg-fS-3aI">
<rect key="frame" x="0.0" y="0.0" width="535" height="134"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<view translatesAutoresizingMaskIntoConstraints="NO" id="uHQ-s7-X3y">
<rect key="frame" x="5" y="5" width="525" height="109"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cpk-ql-oGP">
<rect key="frame" x="163" y="30" width="198" height="29"/>
<buttonCell key="cell" type="bevel" title="Link to existing Ring account" bezelStyle="regularSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Z9K-mD-ucr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="linkExistingRingAccount:" target="-2" id="q5v-67-9eC"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pcF-lX-ex4">
<rect key="frame" x="163" y="62" width="198" height="29"/>
<buttonCell key="cell" type="bevel" title="New Ring account" bezelStyle="regularSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="k1T-a7-yZD">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="createRingAccount:" target="-2" id="Mij-SG-JWQ"/>
</connections>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="UXp-us-Mbl">
<rect key="frame" x="435" y="17" width="72" height="29"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="68" id="BJn-n9-or6"/>
</constraints>
<buttonCell key="cell" type="bevel" title="Cancel" bezelStyle="regularSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="YJ3-7G-cU1">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="onCancel:" target="-2" id="VXk-Vg-8RL"/>
<binding destination="-2" name="hidden" keyPath="self.isCancelable" id="BJD-kc-nuB">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>