diff --git a/Vector.xcodeproj/project.pbxproj b/Vector.xcodeproj/project.pbxproj index d63f39257..d9f3a6255 100644 --- a/Vector.xcodeproj/project.pbxproj +++ b/Vector.xcodeproj/project.pbxproj @@ -171,6 +171,8 @@ F07ECA521D2D730D0060C09F /* main_alias_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F07ECA4F1D2D730D0060C09F /* main_alias_icon.png */; }; F07ECA531D2D730D0060C09F /* main_alias_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F07ECA501D2D730D0060C09F /* main_alias_icon@2x.png */; }; F07ECA541D2D730D0060C09F /* main_alias_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F07ECA511D2D730D0060C09F /* main_alias_icon@3x.png */; }; + F08A543F1D894EDC00A484F1 /* edit_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F08A543D1D894EDC00A484F1 /* edit_icon.png */; }; + F08A54401D894EDC00A484F1 /* edit_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F08A543E1D894EDC00A484F1 /* edit_icon@3x.png */; }; F08BE09E1B87025B00C480FB /* EventFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F08BE09D1B87025B00C480FB /* EventFormatter.m */; }; F08BE0A21B87064000C480FB /* RoomDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = F08BE0A11B87064000C480FB /* RoomDataSource.m */; }; F094A9A81B78D8F000B1FBBF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F094A9A71B78D8F000B1FBBF /* main.m */; }; @@ -515,6 +517,8 @@ F07ECA4F1D2D730D0060C09F /* main_alias_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = main_alias_icon.png; sourceTree = ""; }; F07ECA501D2D730D0060C09F /* main_alias_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "main_alias_icon@2x.png"; sourceTree = ""; }; F07ECA511D2D730D0060C09F /* main_alias_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "main_alias_icon@3x.png"; sourceTree = ""; }; + F08A543D1D894EDC00A484F1 /* edit_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = edit_icon.png; sourceTree = ""; }; + F08A543E1D894EDC00A484F1 /* edit_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "edit_icon@3x.png"; sourceTree = ""; }; F08BE09C1B87025B00C480FB /* EventFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventFormatter.h; sourceTree = ""; }; F08BE09D1B87025B00C480FB /* EventFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventFormatter.m; sourceTree = ""; }; F08BE0A01B87064000C480FB /* RoomDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomDataSource.h; sourceTree = ""; }; @@ -1171,6 +1175,8 @@ F0DD7D1B1B7AA8C900C4BE02 /* Images */ = { isa = PBXGroup; children = ( + F08A543D1D894EDC00A484F1 /* edit_icon.png */, + F08A543E1D894EDC00A484F1 /* edit_icon@3x.png */, F0FE6FB21D66115C0004E747 /* back_icon.png */, F0FE6FB31D66115C0004E747 /* back_icon@2x.png */, F0FE6FB41D66115C0004E747 /* back_icon@3x.png */, @@ -1434,6 +1440,7 @@ F02528F11C11B6FC00E1FE1B /* placeholder@3x.png in Resources */, F0026B5E1C916E68001D2C04 /* priorityHigh.png in Resources */, F02528FB1C11B6FC00E1FE1B /* selection_tick@2x.png in Resources */, + F08A54401D894EDC00A484F1 /* edit_icon@3x.png in Resources */, F001D7631B8207C000A162C3 /* RoomInputToolbarView.xib in Resources */, F02528DE1C11B6FC00E1FE1B /* camera_switch@3x.png in Resources */, F02529091C11B6FC00E1FE1B /* upload_icon.png in Resources */, @@ -1565,6 +1572,7 @@ F0FE6F8E1D6486960004E747 /* call_audio_mute_icon.png in Resources */, F02528EF1C11B6FC00E1FE1B /* placeholder.png in Resources */, F094A9B41B78D8F000B1FBBF /* Main.storyboard in Resources */, + F08A543F1D894EDC00A484F1 /* edit_icon.png in Resources */, 71F7F51E1C23079F00E7ED8F /* ContactTableViewCell.xib in Resources */, F047DBB51C576F2200952DA2 /* AuthenticationViewController.xib in Resources */, F067F2BC1CF6F0EA00F35EE8 /* third_party_licenses.html in Resources */, diff --git a/Vector/AppDelegate.m b/Vector/AppDelegate.m index c9b8220de..a90a94346 100644 --- a/Vector/AppDelegate.m +++ b/Vector/AppDelegate.m @@ -41,6 +41,9 @@ #import "CallViewController.h" +// Uncomment the following line to use local contacts to discover matrix users. +//#define MX_USE_CONTACTS_SERVER_SYNC + //#define MX_CALL_STACK_OPENWEBRTC #ifdef MX_CALL_STACK_OPENWEBRTC #import @@ -313,6 +316,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Configure Google Analytics here if the option is enabled [self startGoogleAnalytics]; + // Configure local contacts management + [MXKContactManager sharedManager].enableFullMatrixIdSyncOnLocalContactsDidLoad = NO; + // Add matrix observers, and initialize matrix sessions if the app is not launched in background. [self initMatrixSessions]; @@ -469,9 +475,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [account resume]; } - // refresh the contacts list - [MXKContactManager sharedManager].enableFullMatrixIdSyncOnLocalContactsDidLoad = NO; - [[MXKContactManager sharedManager] loadLocalContacts]; + // Check if the application is allowed to access the local contacts + if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) + { + // Refresh the local contacts list by reloading it + [[MXKContactManager sharedManager] loadLocalContacts]; + } _isAppForeground = YES; } @@ -1633,9 +1642,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [strongSelf presentCallViewController]; } - // Hide system status bar - [UIApplication sharedApplication].statusBarHidden = YES; - } }]; @@ -1644,9 +1650,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else { [self presentCallViewController]; - - // Hide system status bar - [UIApplication sharedApplication].statusBarHidden = YES; } } @@ -1920,6 +1923,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN BOOL callIsEnded = (callViewController.mxCall.state == MXCallStateEnded); NSLog(@"Call view controller is dismissed (%d)", callIsEnded); + if (callIsEnded) + { + // Restore system status bar + [UIApplication sharedApplication].statusBarHidden = NO; + } + [callViewController dismissViewControllerAnimated:YES completion:^{ callViewController.isPresented = NO; @@ -1939,9 +1948,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { [self removeCallStatusBar]; - // Restore system status bar - [UIApplication sharedApplication].statusBarHidden = NO; - // Release properly currentCallViewController.mxCall.delegate = nil; currentCallViewController.delegate = nil; @@ -2052,6 +2058,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN currentCallViewController.isPresented = YES; + // Hide system status bar + [UIApplication sharedApplication].statusBarHidden = YES; + }]; } else @@ -2060,6 +2069,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN currentCallViewController.isPresented = YES; + // Hide system status bar + [UIApplication sharedApplication].statusBarHidden = YES; + }]; } } diff --git a/Vector/Assets/Images/edit_icon.png b/Vector/Assets/Images/edit_icon.png new file mode 100755 index 000000000..ac8f894f4 Binary files /dev/null and b/Vector/Assets/Images/edit_icon.png differ diff --git a/Vector/Assets/Images/edit_icon@2x.png b/Vector/Assets/Images/edit_icon@2x.png old mode 100644 new mode 100755 index e5a23e14f..0c6da399b Binary files a/Vector/Assets/Images/edit_icon@2x.png and b/Vector/Assets/Images/edit_icon@2x.png differ diff --git a/Vector/Assets/Images/edit_icon@3x.png b/Vector/Assets/Images/edit_icon@3x.png new file mode 100755 index 000000000..65523f80e Binary files /dev/null and b/Vector/Assets/Images/edit_icon@3x.png differ diff --git a/Vector/Assets/en.lproj/Vector.strings b/Vector/Assets/en.lproj/Vector.strings index e157bb4c9..65b4234f3 100644 --- a/Vector/Assets/en.lproj/Vector.strings +++ b/Vector/Assets/en.lproj/Vector.strings @@ -100,7 +100,7 @@ "room_recents_conversations" = "ROOMS"; "room_recents_low_priority" = "LOW PRIORITY"; "room_recents_invites" = "INVITES"; -"room_recents_start_chat_with" = "Invite people"; +"room_recents_start_chat_with" = "Start chat"; "room_recents_create_empty_room" = "Create room"; // Search diff --git a/Vector/ViewController/ContactDetailsViewController.h b/Vector/ViewController/ContactDetailsViewController.h index 3b65a5731..725f86f27 100644 --- a/Vector/ViewController/ContactDetailsViewController.h +++ b/Vector/ViewController/ContactDetailsViewController.h @@ -34,6 +34,7 @@ typedef enum : NSUInteger @property (weak, nonatomic) IBOutlet NSLayoutConstraint *contactAvatarHeaderBackgroundHeightConstraint; @property (weak, nonatomic) IBOutlet UIView *headerView; +@property (weak, nonatomic) IBOutlet UIView *contactAvatarMask; @property (weak, nonatomic) IBOutlet UILabel *contactNameLabel; @property (weak, nonatomic) IBOutlet UIView *contactNameLabelMask; diff --git a/Vector/ViewController/ContactDetailsViewController.m b/Vector/ViewController/ContactDetailsViewController.m index 69382ac60..f6c116fc5 100644 --- a/Vector/ViewController/ContactDetailsViewController.m +++ b/Vector/ViewController/ContactDetailsViewController.m @@ -36,6 +36,9 @@ RoomMemberTitleView* contactTitleView; MXKImageView *contactAvatar; + // HTTP Request + MXHTTPOperation *roomCreationRequest; + /** Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg. */ @@ -117,7 +120,24 @@ contactAvatar = contactTitleView.memberAvatar; contactAvatar.contentMode = UIViewContentModeScaleAspectFill; contactAvatar.backgroundColor = [UIColor clearColor]; - + + // Add tap to show the contact avatar in fullscreen + tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [contactAvatar addGestureRecognizer:tap]; + contactAvatar.userInteractionEnabled = YES; + + // Need to listen tap gesture on the area part of the avatar image that is outside + // of the navigation bar, its parent but smaller view. + tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; + [tap setNumberOfTouchesRequired:1]; + [tap setNumberOfTapsRequired:1]; + [tap setDelegate:self]; + [self.contactAvatarMask addGestureRecognizer:tap]; + self.contactAvatarMask.userInteractionEnabled = YES; + // Add the title view and define edge constraints contactTitleView.translatesAutoresizingMaskIntoConstraints = NO; [self.navigationItem.titleView addSubview:contactTitleView]; @@ -215,6 +235,12 @@ { [super destroy]; + if (roomCreationRequest) + { + [roomCreationRequest cancel]; + roomCreationRequest = nil; + } + [self cancelRegistrationOnContactChangeNotifications]; if (UIApplicationWillChangeStatusBarOrientationNotificationObserver) @@ -236,6 +262,8 @@ - (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + if (contactTitleView) { // Adjust the header height by taking into account the actual position of the member avatar in title view @@ -380,7 +408,7 @@ } else { - image = [UIImage imageNamed:@"placeholder"]; + image = [AvatarGenerator generateAvatarForText:_contact.displayName]; } } @@ -498,6 +526,11 @@ } } } + // Else check whether the contact has been instantiated with an email or a matrix id + else if ([MXTools isEmailAddress:_contact.displayName] || [MXTools isMatrixUserIdentifier:_contact.displayName]) + { + [actionsArray addObject:@(ContactDetailsActionStartChat)]; + } return actionsArray.count; } @@ -642,7 +675,7 @@ NSLog(@"[ContactDetailsViewController] Ignore %@ failed: %@", strongSelf.firstMatrixId, error); // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + [[AppDelegate theDelegate] showErrorAsAlert:error]; }]; @@ -675,7 +708,7 @@ NSLog(@"[ContactDetailsViewController] Unignore %@ failed: %@", self.firstMatrixId, error); // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; + [[AppDelegate theDelegate] showErrorAsAlert:error]; }]; break; @@ -684,10 +717,73 @@ { [self addPendingActionMask]; - [[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:self.firstMatrixId completion:^{ - - [self removePendingActionMask]; - }]; + if (_contact.matrixIdentifiers.count) + { + [[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:self.firstMatrixId completion:^{ + + [self removePendingActionMask]; + }]; + } + else + { + // Create a new room + roomCreationRequest = [self.mainSession createRoom:nil + visibility:kMXRoomDirectoryVisibilityPrivate + roomAlias:nil + topic:nil + success:^(MXRoom *room) { + + roomCreationRequest = nil; + NSString *participantId = _contact.displayName; + + // Is it an email or a Matrix user ID? + if ([MXTools isEmailAddress:participantId]) + { + [room inviteUserByEmail:participantId success:^{ + + NSLog(@"[ContactDetailsViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId); + + } failure:^(NSError *error) { + + NSLog(@"[ContactDetailsViewController] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + else + { + [room inviteUser:participantId success:^{ + + NSLog(@"[ContactDetailsViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId); + + } failure:^(NSError *error) { + + NSLog(@"[ContactDetailsViewController] Invite %@ failed", participantId); + // Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } + + [self removePendingActionMask]; + + [[AppDelegate theDelegate] showRoom:room.state.roomId andEventId:nil withMatrixSession:self.mainSession]; + + } + failure:^(NSError *error) { + + NSLog(@"[ContactDetailsViewController] Create room failed: %@", error); + + roomCreationRequest = nil; + + [self removePendingActionMask]; + + // Notify user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } break; } case ContactDetailsActionStartVoiceCall: @@ -708,42 +804,46 @@ else { // Create a new room - [self.mainSession createRoom:nil - visibility:kMXRoomDirectoryVisibilityPrivate - roomAlias:nil - topic:nil - success:^(MXRoom *room) { - - // Add the user - [room inviteUser:matrixId success:^{ - - // Delay the call in order to be sure that the room is ready - dispatch_async(dispatch_get_main_queue(), ^{ - [room placeCallWithVideo:isVideoCall success:nil failure:nil]; - [self removePendingActionMask]; - }); - - } failure:^(NSError *error) { - - NSLog(@"[ContactDetailsViewController] %@ invitation failed (roomId: %@): %@", matrixId, room.state.roomId, error); - - [self removePendingActionMask]; - - // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; - - }]; - - } failure:^(NSError *error) { - - NSLog(@"[ContactDetailsViewController] Create room failed: %@", error); - - [self removePendingActionMask]; - - // Notify MatrixKit user - [[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error]; - - }]; + roomCreationRequest = [self.mainSession createRoom:nil + visibility:kMXRoomDirectoryVisibilityPrivate + roomAlias:nil + topic:nil + success:^(MXRoom *room) { + + roomCreationRequest = nil; + + // Add the user + [room inviteUser:matrixId success:^{ + + // Delay the call in order to be sure that the room is ready + dispatch_async(dispatch_get_main_queue(), ^{ + [room placeCallWithVideo:isVideoCall success:nil failure:nil]; + [self removePendingActionMask]; + }); + + } failure:^(NSError *error) { + + NSLog(@"[ContactDetailsViewController] %@ invitation failed (roomId: %@): %@", matrixId, room.state.roomId, error); + + [self removePendingActionMask]; + + // Notify MatrixKit user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + + } failure:^(NSError *error) { + + NSLog(@"[ContactDetailsViewController] Create room failed: %@", error); + + roomCreationRequest = nil; + + [self removePendingActionMask]; + + // Notify user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; } break; } @@ -774,6 +874,35 @@ self.contactNameLabel.text = _contact.displayName; } } + else if (view == contactAvatar || view == self.contactAvatarMask) + { + // Show the avatar in full screen + __block MXKImageView * avatarFullScreenView = [[MXKImageView alloc] initWithFrame:CGRectZero]; + avatarFullScreenView.stretchable = YES; + + [avatarFullScreenView setRightButtonTitle:[NSBundle mxk_localizedStringForKey:@"ok"] handler:^(MXKImageView* imageView, NSString* buttonTitle) { + [avatarFullScreenView dismissSelection]; + [avatarFullScreenView removeFromSuperview]; + + avatarFullScreenView = nil; + }]; + + NSString *avatarURL = nil; + if (self.firstMatrixId) + { + MXUser *user = [self.mainSession userWithUserId:self.firstMatrixId]; + avatarURL = [self.mainSession.matrixRestClient urlOfContent:user.avatarUrl]; + } + + // TODO: Display the orignal contact avatar when the contast is not a Matrix user + + [avatarFullScreenView setImageURL:avatarURL + withType:nil + andImageOrientation:UIImageOrientationUp + previewImage:contactAvatar.image]; + + [avatarFullScreenView showFullScreen]; + } } @end diff --git a/Vector/ViewController/ContactDetailsViewController.xib b/Vector/ViewController/ContactDetailsViewController.xib index acad9b897..e6b144581 100644 --- a/Vector/ViewController/ContactDetailsViewController.xib +++ b/Vector/ViewController/ContactDetailsViewController.xib @@ -1,5 +1,5 @@ - + @@ -10,6 +10,7 @@ + @@ -33,6 +34,13 @@ + + + + + + +