Files
bundesmessenger-ios/Riot/Modules/MatrixKit/Views/DeviceView/MXKDeviceView.m

482 lines
24 KiB
Objective-C

/*
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKDeviceView.h"
#import "NSBundle+MatrixKit.h"
#import "MXKConstants.h"
#import "MXKSwiftHeader.h"
static NSAttributedString *verticalWhitespace = nil;
@interface MXKDeviceView ()
{
/**
The displayed device
*/
MXDevice *mxDevice;
/**
The matrix session.
*/
MXSession *mxSession;
/**
The current alert
*/
UIAlertController *currentAlert;
/**
Current request in progress.
*/
MXHTTPOperation *mxCurrentOperation;
}
@end
@implementation MXKDeviceView
+ (UINib *)nib
{
// Check whether a nib file is available
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
if (path)
{
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
}
return [UINib nibWithNibName:NSStringFromClass([MXKDeviceView class]) bundle:[NSBundle mxk_bundleForClass:[MXKDeviceView class]]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
// Add tap recognizer to discard the view on bg view tap
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onBgViewTap:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[self.bgView addGestureRecognizer:tap];
// Localize string
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateNormal];
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateHighlighted];
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateNormal];
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateHighlighted];
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateNormal];
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateHighlighted];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Scroll to the top the text view content
self.textView.contentOffset = CGPointZero;
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
_defaultTextColor = [UIColor blackColor];
// Add shadow on added view
_containerView.layer.cornerRadius = 5;
_containerView.layer.shadowOffset = CGSizeMake(0, 1);
_containerView.layer.shadowOpacity = 0.5f;
}
#pragma mark -
- (void)removeFromSuperviewDidUpdate:(BOOL)isUpdated
{
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
if (mxCurrentOperation)
{
[mxCurrentOperation cancel];
mxCurrentOperation = nil;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(dismissDeviceView:didUpdate:)])
{
[self.delegate dismissDeviceView:self didUpdate:isUpdated];
}
else
{
[self removeFromSuperview];
}
}
- (instancetype)initWithDevice:(MXDevice*)device andMatrixSession:(MXSession*)session
{
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
if (self)
{
mxDevice = device;
mxSession = session;
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
if (mxDevice)
{
// Device information
NSMutableAttributedString *deviceInformationString = [[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsTitle]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsName]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:device.displayName.length ? device.displayName : @""
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsIdentifier]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:device.deviceId
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:[MatrixKitL10n deviceDetailsLastSeen]
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
NSDate *lastSeenDate = [NSDate dateWithTimeIntervalSince1970:device.lastSeenTs/1000];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
NSString *lastSeen = [MatrixKitL10n deviceDetailsLastSeenFormat:device.lastSeenIp :[dateFormatter stringFromDate:lastSeenDate]];
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
initWithString:lastSeen
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
self.textView.attributedText = deviceInformationString;
}
else
{
_textView.text = nil;
}
// Hide potential activity indicator
[_activityIndicator stopAnimating];
}
return self;
}
- (void)dealloc
{
mxDevice = nil;
mxSession = nil;
}
+ (NSAttributedString *)verticalWhitespace
{
if (verticalWhitespace == nil)
{
verticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}];
}
return verticalWhitespace;
}
#pragma mark - Actions
- (IBAction)onBgViewTap:(UITapGestureRecognizer*)sender
{
[self removeFromSuperviewDidUpdate:NO];
}
- (IBAction)onButtonPressed:(id)sender
{
if (sender == _cancelButton)
{
[self removeFromSuperviewDidUpdate:NO];
}
else if (sender == _renameButton)
{
[self renameDevice];
}
else if (sender == _deleteButton)
{
[self deleteDevice];
}
}
#pragma mark -
- (void)renameDevice
{
if (!self.delegate)
{
// Ignore
MXLogDebug(@"[MXKDeviceView] Rename device failed, delegate is missing");
return;
}
// Prompt the user to enter a device name.
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsRenamePromptTitle]
message:[MatrixKitL10n deviceDetailsRenamePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
if (weakSelf)
{
typeof(self) self = weakSelf;
textField.text = self->mxDevice.displayName;
}
}];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
NSString *text = [self->currentAlert textFields].firstObject.text;
self->currentAlert = nil;
[self.activityIndicator startAnimating];
self->mxCurrentOperation = [self->mxSession.matrixRestClient setDeviceName:text forDeviceId:self->mxDevice.deviceId success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxCurrentOperation = nil;
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:YES];
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Rename device (%@) failed", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:NO];
}
}];
}
}]];
[self.delegate deviceView:self presentAlertController:currentAlert];
}
- (void)deleteDevice
{
if (!self.delegate)
{
// Ignore
MXLogDebug(@"[MXKDeviceView] Delete device failed, delegate is missing");
return;
}
// Get an authentication session to prepare device deletion
[self.activityIndicator startAnimating];
mxCurrentOperation = [mxSession.matrixRestClient getSessionToDeleteDeviceByDeviceId:mxDevice.deviceId success:^(MXAuthenticationSession *authSession) {
self->mxCurrentOperation = nil;
// Check whether the password based type is supported
BOOL isPasswordBasedTypeSupported = NO;
for (MXLoginFlow *loginFlow in authSession.flows)
{
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword] || [loginFlow.stages indexOfObject:kMXLoginFlowTypePassword] != NSNotFound)
{
isPasswordBasedTypeSupported = YES;
break;
}
}
if (isPasswordBasedTypeSupported && authSession.session)
{
// Prompt for a password
[self->currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
// Prompt the user before deleting the device.
self->currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsDeletePromptTitle] message:[MatrixKitL10n deviceDetailsDeletePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
[self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDefault;
}];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self.activityIndicator stopAnimating];
}
}]];
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n submit]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
UITextField *textField = [self->currentAlert textFields].firstObject;
self->currentAlert = nil;
NSString *userId = self->mxSession.myUser.userId;
NSDictionary *authParams;
// Sanity check
if (userId)
{
authParams = @{@"session":authSession.session,
@"user": userId,
@"password": textField.text,
@"type": kMXLoginFlowTypePassword};
}
self->mxCurrentOperation = [self->mxSession.matrixRestClient deleteDeviceByDeviceId:self->mxDevice.deviceId authParams:authParams success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->mxCurrentOperation = nil;
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:YES];
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
[self removeFromSuperviewDidUpdate:NO];
}
}];
}
}]];
[self.delegate deviceView:self presentAlertController:self->currentAlert];
}
else
{
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, auth session flow type is not supported", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
}
} failure:^(NSError *error) {
self->mxCurrentOperation = nil;
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, unable to get auth session", self->mxDevice.deviceId);
[self.activityIndicator stopAnimating];
// Notify MatrixKit user
NSString *myUserId = self->mxSession.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
@end