Finish voice_over

This commit is contained in:
manuroe
2019-09-20 17:25:40 +02:00
15 changed files with 297 additions and 15 deletions
+9
View File
@@ -1,3 +1,12 @@
Changes in 0.9.5 (2019-09-20)
===============================================
Bug fix:
* VoiceOver: RoomVC: Fix some missing accessibility labels for buttons (#2722).
* VoiceOver: RoomVC: Make VoiceOver focus on the contextual menu when selecting an event (#2721).
* VoiceOver: RoomVC: Do not lose the focus on the timeline when paginating (with 3 fingers) (#2720).
* VoiceOver: RoomVC: No VoiceOver on media (#2726).
Changes in 0.9.4 (2019-09-13)
===============================================
+16
View File
@@ -250,6 +250,7 @@
// Chat
"room_jump_to_first_unread" = "Jump to first unread message";
"room_accessiblity_scroll_to_bottom" = "Scroll to bottom";
"room_new_message_notification" = "%d new message";
"room_new_messages_notification" = "%d new messages";
"room_one_user_is_typing" = "%@ is typing…";
@@ -317,6 +318,18 @@
"room_resource_usage_limit_reached_message_2" = "some users will not be able to log in.";
"room_resource_usage_limit_reached_message_contact_3" = " to get this limit increased.";
"room_message_edits_history_title" = "Message edits";
"room_accessibility_search" = "Search";
"room_accessibility_integrations" = "Integrations";
"room_accessibility_upload" = "Upload";
"room_accessibility_call" = "Call";
"room_accessibility_hangup" = "Hang up";
"media_type_accessibility_image" = "Image";
"media_type_accessibility_audio" = "Audio";
"media_type_accessibility_video" = "Video";
"media_type_accessibility_location" = "Location";
"media_type_accessibility_file" = "File";
"media_type_accessibility_sticker" = "Sticker";
// Unknown devices
"unknown_devices_alert_title" = "Room contains unknown devices";
@@ -677,6 +690,9 @@
"widget_integration_missing_user_id" = "Missing user_id in request.";
"widget_integration_room_not_visible" = "Room %@ is not visible.";
// Widget Picker
"widget_picker_title" = "Integrations";
// Share extension
"share_extension_auth_prompt" = "Login in the main app to share content";
"share_extension_failed_to_encrypt" = "Failed to send. Check in the main app the encryption settings for this room";
+52
View File
@@ -1374,6 +1374,30 @@ internal enum VectorL10n {
internal static var mediaPickerTitle: String {
return VectorL10n.tr("Vector", "media_picker_title")
}
/// Audio
internal static var mediaTypeAccessibilityAudio: String {
return VectorL10n.tr("Vector", "media_type_accessibility_audio")
}
/// File
internal static var mediaTypeAccessibilityFile: String {
return VectorL10n.tr("Vector", "media_type_accessibility_file")
}
/// Image
internal static var mediaTypeAccessibilityImage: String {
return VectorL10n.tr("Vector", "media_type_accessibility_image")
}
/// Location
internal static var mediaTypeAccessibilityLocation: String {
return VectorL10n.tr("Vector", "media_type_accessibility_location")
}
/// Sticker
internal static var mediaTypeAccessibilitySticker: String {
return VectorL10n.tr("Vector", "media_type_accessibility_sticker")
}
/// Video
internal static var mediaTypeAccessibilityVideo: String {
return VectorL10n.tr("Vector", "media_type_accessibility_video")
}
/// The Internet connection appears to be offline.
internal static var networkOfflinePrompt: String {
return VectorL10n.tr("Vector", "network_offline_prompt")
@@ -1462,6 +1486,30 @@ internal enum VectorL10n {
internal static var retry: String {
return VectorL10n.tr("Vector", "retry")
}
/// Call
internal static var roomAccessibilityCall: String {
return VectorL10n.tr("Vector", "room_accessibility_call")
}
/// Hang up
internal static var roomAccessibilityHangup: String {
return VectorL10n.tr("Vector", "room_accessibility_hangup")
}
/// Integrations
internal static var roomAccessibilityIntegrations: String {
return VectorL10n.tr("Vector", "room_accessibility_integrations")
}
/// Search
internal static var roomAccessibilitySearch: String {
return VectorL10n.tr("Vector", "room_accessibility_search")
}
/// Upload
internal static var roomAccessibilityUpload: String {
return VectorL10n.tr("Vector", "room_accessibility_upload")
}
/// Scroll to bottom
internal static var roomAccessiblityScrollToBottom: String {
return VectorL10n.tr("Vector", "room_accessiblity_scroll_to_bottom")
}
/// Take photo or video
internal static var roomActionCamera: String {
return VectorL10n.tr("Vector", "room_action_camera")
@@ -2974,6 +3022,10 @@ internal enum VectorL10n {
internal static var widgetNoPowerToManage: String {
return VectorL10n.tr("Vector", "widget_no_power_to_manage")
}
/// Integrations
internal static var widgetPickerTitle: String {
return VectorL10n.tr("Vector", "widget_picker_title")
}
/// You don't currently have any stickerpacks enabled.
internal static var widgetStickerPickerNoStickerpacksAlert: String {
return VectorL10n.tr("Vector", "widget_sticker_picker_no_stickerpacks_alert")
@@ -40,7 +40,7 @@
mxSession = theMXSession;
roomId = theRoomId;
_alertController = [UIAlertController alertControllerWithTitle:@"Matrix Apps"
_alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"widget_picker_title", @"Vector", nil)
message:nil
preferredStyle:UIAlertControllerStyleAlert];
}
@@ -90,4 +90,9 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
- (BOOL)showAllReactionsForEvent:(NSString*)eventId;
- (void)setShowAllReactions:(BOOL)showAllReactions forEvent:(NSString*)eventId;
#pragma mark - Accessibility
- (NSString*)accessibilityLabel;
@end
@@ -698,4 +698,58 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
}
}
- (NSString *)accessibilityLabel
{
NSString *accessibilityLabel;
// Only media require manual handling for accessibility
if (self.attachment)
{
NSString *mediaName = [self accessibilityLabelForAttachmentType:self.attachment.type];
MXJSONModelSetString(accessibilityLabel, self.events.firstObject.content[@"body"]);
if (accessibilityLabel)
{
accessibilityLabel = [NSString stringWithFormat:@"%@ %@", mediaName, accessibilityLabel];
}
else
{
accessibilityLabel = mediaName;
}
}
return accessibilityLabel;
}
- (NSString*)accessibilityLabelForAttachmentType:(MXKAttachmentType)attachmentType
{
NSString *accessibilityLabel;
switch (attachmentType)
{
case MXKAttachmentTypeImage:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_image", @"Vector", nil);
break;
case MXKAttachmentTypeAudio:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_audio", @"Vector", nil);
break;
case MXKAttachmentTypeVideo:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_video", @"Vector", nil);
break;
case MXKAttachmentTypeLocation:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_location", @"Vector", nil);
break;
case MXKAttachmentTypeFile:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_file", @"Vector", nil);
break;
case MXKAttachmentTypeSticker:
accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_sticker", @"Vector", nil);
break;
default:
accessibilityLabel = @"";
break;
}
return accessibilityLabel;
}
@end
@@ -93,20 +93,30 @@ final class ContextualMenuItemView: UIView, NibOwnerLoadable {
}
// MARK: - Public
func fill(title: String, image: UIImage?) {
self.originalImage = image?.withRenderingMode(.alwaysTemplate)
self.titleLabel.text = title
self.updateView()
}
func fill(menuItem: RoomContextualMenuItem) {
self.fill(title: menuItem.title, image: menuItem.image)
self.setupAccessibility(title: menuItem.title, isEnabled: menuItem.isEnabled)
self.action = menuItem.action
self.isEnabled = menuItem.isEnabled
}
// MARK: - Private
private func fill(title: String, image: UIImage?) {
self.originalImage = image?.withRenderingMode(.alwaysTemplate)
self.titleLabel.text = title
self.updateView()
}
private func setupAccessibility(title: String, isEnabled: Bool) {
self.isAccessibilityElement = true
self.accessibilityLabel = title
self.accessibilityTraits = .button
if !isEnabled {
self.accessibilityTraits.insert(.notEnabled)
}
}
private func setupGestureRecognizer() {
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
@@ -117,11 +117,15 @@ final class RoomContextualMenuViewController: UIViewController, Themable {
func showMenuToolbar() {
self.menuToolbarViewBottomConstraint.constant = 0
self.menuToolbarView.alpha = 1
// Force VoiceOver to focus on the menu bar actions
UIAccessibility.post(notification: .screenChanged, argument: self.menuToolbarView)
}
func hideMenuToolbar() {
self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant
self.menuToolbarView.alpha = 0
UIAccessibility.post(notification: .screenChanged, argument: nil)
}
func prepareReactionsMenuAnimations() {
@@ -196,6 +196,8 @@
if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class])
{
MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell;
[self resetAccessibilityForCell:bubbleCell];
RoomBubbleCellData *cellData = (RoomBubbleCellData*)bubbleCell.bubbleData;
NSArray *bubbleComponents = cellData.bubbleComponents;
@@ -507,6 +509,8 @@
// Auto animate the sticker in case of animated gif
bubbleCell.isAutoAnimatedGif = (cellData.attachment && cellData.attachment.type == MXKAttachmentTypeSticker);
[self setupAccessibilityForCell:bubbleCell withCellData:cellData];
}
return cell;
@@ -563,6 +567,35 @@
[self sendVideo:videoLocalURL withThumbnail:videoThumbnail success:success failure:failure];
}
#pragma - Accessibility
- (void)setupAccessibilityForCell:(MXKRoomBubbleTableViewCell *)cell withCellData:(RoomBubbleCellData*)cellData
{
// Set accessibility only on media. Let VoiceOver automatically manages text messages
if (cellData.attachment)
{
NSString *accessibilityLabel = [cellData accessibilityLabel];
if (cell.messageTextView.text.length)
{
// Files are presented as text with link
cell.messageTextView.accessibilityLabel = accessibilityLabel;
cell.messageTextView.isAccessibilityElement = YES;
}
else
{
cell.attachmentView.accessibilityLabel = accessibilityLabel;
cell.attachmentView.isAccessibilityElement = YES;
}
}
}
- (void)resetAccessibilityForCell:(MXKRoomBubbleTableViewCell *)cell
{
cell.messageTextView.accessibilityLabel = nil;
cell.attachmentView.accessibilityLabel = nil;
}
#pragma mark - BubbleReactionsViewModelDelegate
- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didAddReaction:(MXReactionCount *)reactionCount forEventId:(NSString *)eventId
+91 -1
View File
@@ -123,7 +123,7 @@
#import "Riot-Swift.h"
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, UIScrollViewAccessibilityDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate, EmojiPickerCoordinatorBridgePresenterDelegate,
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate>
{
@@ -813,6 +813,92 @@
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
#pragma mark - Accessibility
// Handle scrolling when VoiceOver is on because it does not work well if we let the system do:
// VoiceOver loses the focus on the tableview
- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
{
BOOL canScroll = YES;
// Scroll by one page
CGFloat tableViewHeight = self.bubblesTableView.frame.size.height;
CGPoint offset = self.bubblesTableView.contentOffset;
switch (direction)
{
case UIAccessibilityScrollDirectionUp:
offset.y -= tableViewHeight;
break;
case UIAccessibilityScrollDirectionDown:
offset.y += tableViewHeight;
break;
default:
break;
}
if (offset.y < 0 && ![self.roomDataSource.timeline canPaginate:MXTimelineDirectionBackwards])
{
// Can't paginate more. Let's stick on the first item
UIView *focusedView = [self firstCellWithAccessibilityDataInCells:self.bubblesTableView.visibleCells.objectEnumerator];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, focusedView);
canScroll = NO;
}
else if (offset.y > self.bubblesTableView.contentSize.height - tableViewHeight
&& ![self.roomDataSource.timeline canPaginate:MXTimelineDirectionForwards])
{
// Can't paginate more. Let's stick on the last item with accessibility
UIView *focusedView = [self firstCellWithAccessibilityDataInCells:self.bubblesTableView.visibleCells.reverseObjectEnumerator];
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, focusedView);
canScroll = NO;
}
else
{
// Disable VoiceOver while scrolling
self.bubblesTableView.accessibilityElementsHidden = YES;
[self.bubblesTableView setContentOffset:offset animated:NO];
NSEnumerator<UITableViewCell*> *cells;
if (direction == UIAccessibilityScrollDirectionUp)
{
cells = self.bubblesTableView.visibleCells.objectEnumerator;
}
else
{
cells = self.bubblesTableView.visibleCells.reverseObjectEnumerator;
}
UIView *cell = [self firstCellWithAccessibilityDataInCells:cells];
self.bubblesTableView.accessibilityElementsHidden = NO;
// Force VoiceOver to focus on a visible item
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, cell);
}
// If we cannot scroll, let VoiceOver indicates the border
return canScroll;
}
- (UIView*)firstCellWithAccessibilityDataInCells:(NSEnumerator<UITableViewCell*>*)cells
{
UIView *view;
for (UITableViewCell *cell in cells)
{
if (![cell isKindOfClass:[RoomEmptyBubbleCell class]])
{
view = cell;
break;
}
}
return view;
}
#pragma mark - Override MXKRoomViewController
- (void)onMatrixSessionChange
@@ -1372,12 +1458,16 @@
icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.navigationItem.rightBarButtonItems[1].image = icon;
self.navigationItem.rightBarButtonItems[1].accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil);
}
else
{
// Reset original icon
self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"apps-icon"];
self.navigationItem.rightBarButtonItems[1].accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil);
}
self.navigationItem.rightBarButtonItems.firstObject.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_search", @"Vector", nil);
}
// Do not change title view class here if the expanded header is visible.
@@ -307,6 +307,11 @@
self.iconImageView.image = [UIImage imageNamed:@"scrolldown"];
}
self.iconImageView.hidden = NO;
// Make VoiceOver consider it as a button
self.iconImageView.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessiblity_scroll_to_bottom", @"Vector", nil);
self.iconImageView.isAccessibilityElement = YES;
self.iconImageView.accessibilityTraits = UIAccessibilityTraitButton;
if (onIconTapGesture)
{
@@ -92,6 +92,10 @@
growingTextView.tintColor = ThemeService.shared.theme.tintColor;
growingTextView.internalTextView.keyboardAppearance = ThemeService.shared.theme.keyboardAppearance;
self.attachMediaButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_upload", @"Vector", nil);
self.voiceCallButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_call", @"Vector", nil);
self.hangupCallButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_hangup", @"Vector", nil);
}
#pragma mark -
+2 -2
View File
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>CFBundleVersion</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
+2 -2
View File
@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>CFBundleVersion</key>
<string>0.9.4</string>
<string>0.9.5</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>