diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index 84ecb908a..a9bea1d96 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -4,7 +4,8 @@ version = "1.3"> + buildImplicitDependencies = "YES" + runPostActionsOnFailure = "NO"> String { - return VectorL10n.tr("Vector", "pin_protection_settings_section_header_x", p1) + internal static func pinProtectionSettingsSectionHeaderWithBiometrics(_ p1: String) -> String { + return VectorL10n.tr("Vector", "pin_protection_settings_section_header_with_biometrics", p1) } /// Preview internal static var preview: String { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 301c568ba..f21972c48 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -631,7 +631,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self.updateRoomReadMarker = NO; isAppeared = NO; - [VoiceMessageMediaServiceProvider.sharedProvider stopAllServices]; + [VoiceMessageMediaServiceProvider.sharedProvider pauseAllServices]; } - (void)viewDidAppear:(BOOL)animated @@ -4424,27 +4424,48 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; else if (tappedView == previewHeader.rightButton) { // 'Join' button has been pressed - if (roomPreviewData) + if (!roomPreviewData) { - // Attempt to join the room (keep reference on the potential eventId, the preview data will be removed automatically in case of success). - NSString *eventId = roomPreviewData.eventId; + [self joinRoom:^(MXKRoomViewControllerJoinRoomResult result) { + switch (result) + { + case MXKRoomViewControllerJoinRoomResultSuccess: + [self refreshRoomTitle]; + break; + case MXKRoomViewControllerJoinRoomResultFailureRoomEmpty: + [self declineRoomInvitation]; + break; + default: + break; + } + }]; - // We promote here join by room alias instead of room id when an alias is available. - NSString *roomIdOrAlias = roomPreviewData.roomId; + return; + } + + // Attempt to join the room (keep reference on the potential eventId, the preview data will be removed automatically in case of success). + NSString *eventId = roomPreviewData.eventId; + + // We promote here join by room alias instead of room id when an alias is available. + NSString *roomIdOrAlias = roomPreviewData.roomId; + + if (roomPreviewData.roomCanonicalAlias.length) + { + roomIdOrAlias = roomPreviewData.roomCanonicalAlias; + } + else if (roomPreviewData.roomAliases.count) + { + roomIdOrAlias = roomPreviewData.roomAliases.firstObject; + } + + // Note in case of simple link to a room the signUrl param is nil + [self joinRoomWithRoomIdOrAlias:roomIdOrAlias viaServers:roomPreviewData.viaServers + andSignUrl:roomPreviewData.emailInvitation.signUrl + completion:^(MXKRoomViewControllerJoinRoomResult result) { - if (roomPreviewData.roomCanonicalAlias.length) + switch (result) { - roomIdOrAlias = roomPreviewData.roomCanonicalAlias; - } - else if (roomPreviewData.roomAliases.count) - { - roomIdOrAlias = roomPreviewData.roomAliases.firstObject; - } - - // Note in case of simple link to a room the signUrl param is nil - [self joinRoomWithRoomIdOrAlias:roomIdOrAlias viaServers:roomPreviewData.viaServers andSignUrl:roomPreviewData.emailInvitation.signUrl completion:^(BOOL succeed) { - - if (succeed) + case MXKRoomViewControllerJoinRoomResultSuccess: { // If an event was specified, replace the datasource by a non live datasource showing the event if (eventId) @@ -4473,33 +4494,47 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [self refreshRoomTitle]; [self refreshRoomInputToolbar]; } + break; } - - }]; - } - else - { - [self joinRoom:^(BOOL succeed) { - - if (succeed) - { - [self refreshRoomTitle]; - } - - }]; - } + case MXKRoomViewControllerJoinRoomResultFailureRoomEmpty: + [self declineRoomInvitation]; + break; + default: + break; + } + }]; } else if (tappedView == previewHeader.leftButton) { - // 'Decline' button has been pressed - if (roomPreviewData) - { - [self roomPreviewDidTapCancelAction]; - } - else - { - [self leaveRoom]; - } + [self declineRoomInvitation]; + } +} + +- (void)declineRoomInvitation +{ + // 'Decline' button has been pressed + if (roomPreviewData) + { + [self roomPreviewDidTapCancelAction]; + } + else + { + [self startActivityIndicator]; + + [self.roomDataSource.room leave:^{ + + [self stopActivityIndicator]; + + // We remove the current view controller. + // Pop to homes view controller + [[AppDelegate theDelegate] restoreInitialDisplay:^{}]; + + } failure:^(NSError *error) { + + [self stopActivityIndicator]; + MXLogDebug(@"[RoomVC] Failed to reject an invited room (%@) failed", self.roomDataSource.room.roomId); + + }]; } } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift index 1cca80dd3..3037c67d0 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift @@ -31,7 +31,13 @@ import MediaPlayer private var displayLink: CADisplayLink! - // Retain currently playing audio player so it doesn't stop playing on timeline cell reuse + + + // Retain active audio players(playing or paused) so it doesn't stop playing on timeline cell reuse + // and we can pause/resume players on switching rooms. + private var activeAudioPlayers: Set + + // Keep reference to currently playing player for remote control. private var currentlyPlayingAudioPlayer: VoiceMessageAudioPlayer? @objc public static let sharedProvider = VoiceMessageMediaServiceProvider() @@ -87,7 +93,7 @@ import MediaPlayer private override init() { audioPlayers = NSMapTable(valueOptions: .weakMemory) audioRecorders = NSHashTable(options: .weakMemory) - + activeAudioPlayers = Set() super.init() displayLink = CADisplayLink(target: WeakTarget(self, selector: #selector(handleDisplayLinkTick)), selector: WeakTarget.triggerSelector) @@ -113,16 +119,17 @@ import MediaPlayer return audioRecorder } - @objc func stopAllServices() { - stopAllServicesExcept(nil) + @objc func pauseAllServices() { + pauseAllServicesExcept(nil) } // MARK: - VoiceMessageAudioPlayerDelegate func audioPlayerDidStartPlaying(_ audioPlayer: VoiceMessageAudioPlayer) { currentlyPlayingAudioPlayer = audioPlayer + activeAudioPlayers.insert(audioPlayer) setUpRemoteCommandCenter() - stopAllServicesExcept(audioPlayer) + pauseAllServicesExcept(audioPlayer) } func audioPlayerDidStopPlaying(_ audioPlayer: VoiceMessageAudioPlayer) { @@ -130,6 +137,7 @@ import MediaPlayer currentlyPlayingAudioPlayer = nil tearDownRemoteCommandCenter() } + activeAudioPlayers.remove(audioPlayer) } func audioPlayerDidFinishPlaying(_ audioPlayer: VoiceMessageAudioPlayer) { @@ -137,17 +145,18 @@ import MediaPlayer currentlyPlayingAudioPlayer = nil tearDownRemoteCommandCenter() } + activeAudioPlayers.remove(audioPlayer) } // MARK: - VoiceMessageAudioRecorderDelegate func audioRecorderDidStartRecording(_ audioRecorder: VoiceMessageAudioRecorder) { - stopAllServicesExcept(audioRecorder) + pauseAllServicesExcept(audioRecorder) } // MARK: - Private - private func stopAllServicesExcept(_ service: AnyObject?) { + private func pauseAllServicesExcept(_ service: AnyObject?) { for audioRecorder in audioRecorders.allObjects { if audioRecorder === service { continue @@ -165,8 +174,7 @@ import MediaPlayer continue } - audioPlayer.stop() - audioPlayer.unloadContent() + audioPlayer.pause() } } diff --git a/Riot/Modules/SetPinCode/SetPinCoordinator.swift b/Riot/Modules/SetPinCode/SetPinCoordinator.swift index 5bc9ea80f..627f14730 100644 --- a/Riot/Modules/SetPinCode/SetPinCoordinator.swift +++ b/Riot/Modules/SetPinCode/SetPinCoordinator.swift @@ -145,7 +145,7 @@ extension SetPinCoordinator: EnterPinCodeCoordinatorDelegate { func enterPinCodeCoordinator(_ coordinator: EnterPinCodeCoordinatorType, didCompleteWithPin pin: String) { storePin(pin) - if pinCodePreferences.forcePinProtection && pinCodePreferences.isBiometricsAvailable { + if pinCodePreferences.forcePinProtection && pinCodePreferences.isBiometricsAvailable && !pinCodePreferences.isBiometricsSet { viewMode = .setupBiometricsAfterLogin setRootCoordinator(createSetupBiometricsCoordinator()) } else { diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index b0191ae74..8d22fe8d2 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -292,14 +292,21 @@ TableViewSectionsDelegate> Section *pinCodeSection = [Section sectionWithTag:SECTION_PIN_CODE]; // Header title - NSString *pinCodeSectionHeaderTitleFormat = NSLocalizedStringFromTable(@"pin_protection_settings_section_header_x", @"Vector", nil); - NSString *pinCodeSectionHeaderTitle = [NSString stringWithFormat:pinCodeSectionHeaderTitleFormat, [PinCodePreferences shared].localizedBiometricsName]; - pinCodeSection.headerTitle = pinCodeSectionHeaderTitle; + if ([PinCodePreferences shared].isBiometricsAvailable) + { + NSString *pinCodeSectionHeaderTitleFormat = NSLocalizedStringFromTable(@"pin_protection_settings_section_header_with_biometrics", @"Vector", nil); + NSString *pinCodeSectionHeaderTitle = [NSString stringWithFormat:pinCodeSectionHeaderTitleFormat, [PinCodePreferences shared].localizedBiometricsName]; + pinCodeSection.headerTitle = pinCodeSectionHeaderTitle; + } else { + pinCodeSection.headerTitle = NSLocalizedStringFromTable(@"pin_protection_settings_section_header", @"Vector", nil);; + } // Rows [pinCodeSection addRowWithTag:PIN_CODE_SETTING]; [pinCodeSection addRowWithTag:PIN_CODE_DESCRIPTION]; - if ([PinCodePreferences shared].isPinSet) { + + if ([PinCodePreferences shared].isPinSet) + { [pinCodeSection addRowWithTag:PIN_CODE_CHANGE]; } @@ -1183,17 +1190,17 @@ TableViewSectionsDelegate> - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSIndexPath *tagsIndexPath = [self.tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath]; - NSInteger section = tagsIndexPath.section; - NSInteger row = tagsIndexPath.row; + NSInteger sectionTag = tagsIndexPath.section; + NSInteger rowTag = tagsIndexPath.row; // set the cell to a default value to avoid application crashes UITableViewCell *cell = [[UITableViewCell alloc] init]; cell.backgroundColor = [UIColor redColor]; MXSession* session = self.mainSession; - if (section == SECTION_PIN_CODE) + if (sectionTag == SECTION_PIN_CODE) { - if (indexPath.row == PIN_CODE_SETTING) + if (rowTag == PIN_CODE_SETTING) { if ([PinCodePreferences shared].forcePinProtection) { @@ -1213,7 +1220,7 @@ TableViewSectionsDelegate> cell.selectionStyle = UITableViewCellSelectionStyleNone; } - else if (indexPath.row == PIN_CODE_DESCRIPTION) + else if (rowTag == PIN_CODE_DESCRIPTION) { if ([PinCodePreferences shared].isPinSet) { @@ -1225,11 +1232,11 @@ TableViewSectionsDelegate> cell = [self descriptionCellForTableView:tableView withText:nil]; } } - else if (indexPath.row == PIN_CODE_CHANGE) + else if (rowTag == PIN_CODE_CHANGE) { cell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"pin_protection_settings_change_pin", @"Vector", nil) action:@selector(changePinCode: ) forTableView:tableView atIndexPath:indexPath]; } - else if (indexPath.row == PIN_CODE_BIOMETRICS) + else if (rowTag == PIN_CODE_BIOMETRICS) { MXKTableViewCellWithLabelAndSwitch *switchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; @@ -1242,11 +1249,11 @@ TableViewSectionsDelegate> cell = switchCell; } } - else if (section == SECTION_CRYPTO_SESSIONS) + else if (sectionTag == SECTION_CRYPTO_SESSIONS) { if (self.showLoadingDevicesInformation) { - if (indexPath.row == 0) + if (rowTag == 0) { cell = [self descriptionCellForTableView:tableView withText:NSLocalizedStringFromTable(@"security_settings_crypto_sessions_loading", @"Vector", nil) ]; @@ -1259,11 +1266,11 @@ TableViewSectionsDelegate> } else { - if (row < devicesArray.count) + if (rowTag < devicesArray.count) { - cell = [self deviceCellWithDevice:devicesArray[row] forTableView:tableView]; + cell = [self deviceCellWithDevice:devicesArray[rowTag] forTableView:tableView]; } - else if (row == devicesArray.count) + else if (rowTag == devicesArray.count) { cell = [self descriptionCellForTableView:tableView withText:NSLocalizedStringFromTable(@"security_settings_crypto_sessions_description_2", @"Vector", nil) ]; @@ -1271,9 +1278,9 @@ TableViewSectionsDelegate> } } } - else if (section == SECTION_SECURE_BACKUP) + else if (sectionTag == SECTION_SECURE_BACKUP) { - cell = [secureBackupSection cellForRowAtRow:row]; + cell = [secureBackupSection cellForRowAtRow:rowTag]; } #ifdef CROSS_SIGNING_AND_BACKUP_DEV else if (section == SECTION_KEYBACKUP) @@ -1281,9 +1288,9 @@ TableViewSectionsDelegate> cell = [keyBackupSection cellForRowAtRow:row]; } #endif - else if (section == SECTION_CROSSSIGNING) + else if (sectionTag == SECTION_CROSSSIGNING) { - switch (row) + switch (rowTag) { case CROSSSIGNING_INFO: { @@ -1300,9 +1307,9 @@ TableViewSectionsDelegate> break; } } - else if (section == SECTION_CRYPTOGRAPHY) + else if (sectionTag == SECTION_CRYPTOGRAPHY) { - switch (row) + switch (rowTag) { case CRYPTOGRAPHY_INFO: { @@ -1322,9 +1329,9 @@ TableViewSectionsDelegate> } } } - else if (section == SECTION_ADVANCED) + else if (sectionTag == SECTION_ADVANCED) { - switch (row) + switch (rowTag) { case ADVANCED_BLACKLIST_UNVERIFIED_DEVICES: { diff --git a/changelog.d/4415.bugfix b/changelog.d/4415.bugfix new file mode 100644 index 000000000..0e3e290d2 --- /dev/null +++ b/changelog.d/4415.bugfix @@ -0,0 +1 @@ +Cannot disable Face ID after disabling pin. \ No newline at end of file diff --git a/changelog.d/4461.bugfix b/changelog.d/4461.bugfix new file mode 100644 index 000000000..830245c1f --- /dev/null +++ b/changelog.d/4461.bugfix @@ -0,0 +1 @@ +Fixes "PIN & (NULL)" security section header when device biometrics are not available or not enrolled into. \ No newline at end of file diff --git a/changelog.d/47773.change b/changelog.d/47773.change new file mode 100644 index 000000000..826cb00bd --- /dev/null +++ b/changelog.d/47773.change @@ -0,0 +1 @@ +Voice Messages: Pause playback when changing rooms while retaining the playback position when re-entering. diff --git a/changelog.d/4830.change b/changelog.d/4830.change new file mode 100644 index 000000000..9ca0cd7fa --- /dev/null +++ b/changelog.d/4830.change @@ -0,0 +1 @@ +Automatically dismissing invites for empty rooms after failing to join. \ No newline at end of file