From 9d7d91df0af0236468a78645da84ae1725922012 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 21 Sep 2017 15:29:46 +0200 Subject: [PATCH 1/3] BF: [iOS11] "Smart [colors] Invert" renders badly in the app https://github.com/vector-im/riot-ios/issues/1524 - the app does its own dark theme and prevents the OS from automatically revert the colors in the app - there are 3 choices on iOS11 for the Riot theme: "auto", "light", "dark". "auto" uses the system settings --- Riot/AppDelegate.m | 6 + Riot/Assets/de.lproj/Vector.strings | 2 - Riot/Assets/en.lproj/Vector.strings | 8 +- Riot/Assets/fr.lproj/Vector.strings | 8 +- Riot/Assets/nl.lproj/Vector.strings | 2 - Riot/Assets/ru.lproj/Vector.strings | 2 - Riot/Utils/RiotDesignValues.m | 21 ++- Riot/ViewController/SettingsViewController.m | 155 ++++++++++++++----- 8 files changed, 151 insertions(+), 53 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 54f72cec1..8654776c4 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -516,6 +516,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [self refreshLocalContacts]; _isAppForeground = YES; + + if (@available(iOS 11.0, *)) + { + // Riot has its own dark theme. Prevent iOS from applying its one + [[UIApplication sharedApplication] keyWindow].accessibilityIgnoresInvertColors = YES; + } [self handleLaunchAnimation]; } diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 2ce61d393..6b8df2779 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -416,8 +416,6 @@ "auth_email_not_found" = "Fehler beim Senden der E-Mail: Die E-Mail-Adresse wurde nicht gefunden"; "settings_user_interface" = "BENUTZEROBERFLÄCHE"; "settings_ui_language" = "Sprache"; -"settings_ui_light_theme" = "Helles Design"; -"settings_ui_dark_theme" = "Dunkles Design"; // Events formatter "event_formatter_member_updates" = "%tu Änderungen der Mitgliedschaft"; "contacts_user_directory_section" = "NUTZER VERZEICHNIS"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 0691ddaa0..cccc4c357 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -340,8 +340,12 @@ //"settings_call_invitations" = "Call invitations"; "settings_ui_language" = "Language"; -"settings_ui_light_theme" = "Light theme"; -"settings_ui_dark_theme" = "Dark theme"; +"settings_ui_theme" = "Theme"; +"settings_ui_theme_auto" = "Auto"; +"settings_ui_theme_light" = "Light"; +"settings_ui_theme_dark" = "Dark"; +"settings_ui_theme_picker_title" = "Select a theme"; +"settings_ui_theme_picker_message" = "\"Auto\" uses your device \"Invert Colours\" settings"; "settings_unignore_user" = "Show all messages from %@?"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 7cf68651c..06bec44d4 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -417,8 +417,12 @@ "auth_phone_in_use" = "Ce numéro de téléphone est déjà utilisé"; "auth_email_not_found" = "Échec lors de l'envoi de l'e-mail : Cette adresse e-mail n'a pas pu être trouvée"; "settings_ui_language" = "Langue"; -"settings_ui_light_theme" = "Thème clair"; -"settings_ui_dark_theme" = "Thème sombre"; +"settings_ui_theme" = "Thème"; +"settings_ui_theme_auto" = "Auto"; +"settings_ui_theme_light" = "Clair"; +"settings_ui_theme_dark" = "Sombre"; +"settings_ui_theme_picker_title" = "Selectionnez un thème"; +"settings_ui_theme_picker_message" = "\"Auto\" utilise le paramètre \"Inverser les couleurs\" de votre appareil"; "collapse" = "réduire"; "auth_untrusted_id_server" = "Le serveur d'identité n'est pas fiable"; "settings_user_interface" = "INTERFACE UTILISATEUR"; diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index b9b994840..6290d9b57 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -440,8 +440,6 @@ "contacts_user_directory_offline_section" = "GEBRUIKERSADRESBOEK (offline)"; "settings_user_interface" = "GEBRUIKERSINTERFACE"; "settings_ui_language" = "Taal"; -"settings_ui_light_theme" = "Lichte thema"; -"settings_ui_dark_theme" = "Donkere thema"; // Read Receipts "read_receipts_list" = "Leesbewijzen Lijst"; "receipt_status_read" = "Lees: "; diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index f9431d161..b4d02c15f 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -262,8 +262,6 @@ "settings_global_settings_info" = "Глобальные настройки уведомлений доступны в вашем %@ веб-клиенте"; "settings_on_denied_notification" = "Уведомления для %@ запрещены, пожалуйста, разрешите их в настройках вашего устройства"; "settings_ui_language" = "Язык"; -"settings_ui_light_theme" = "Светлая тема"; -"settings_ui_dark_theme" = "Темная тема"; "settings_unignore_user" = "Показать все сообщения от %@?"; "settings_labs_e2e_encryption" = "Сквозное шифрование"; "settings_labs_e2e_encryption_prompt_message" = "Чтобы завершить настройку шифрования, вы должны войти в систему еще раз."; diff --git a/Riot/Utils/RiotDesignValues.m b/Riot/Utils/RiotDesignValues.m index cd0ee8756..d52782d32 100644 --- a/Riot/Utils/RiotDesignValues.m +++ b/Riot/Utils/RiotDesignValues.m @@ -112,8 +112,10 @@ UIColor *kRiotDesignSearchBarTintColor = nil; // Observe user interface theme change. [[NSUserDefaults standardUserDefaults] addObserver:[RiotDesignValues sharedInstance] forKeyPath:@"userInterfaceTheme" options:0 context:nil]; [[RiotDesignValues sharedInstance] userInterfaceThemeDidChange]; -} + // Observe "Invert Colours" settings changes (available since iOS 11) + [[NSNotificationCenter defaultCenter] addObserver:[RiotDesignValues sharedInstance] selector:@selector(accessibilityInvertColorsStatusDidChange) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil]; +} - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @@ -123,10 +125,25 @@ UIColor *kRiotDesignSearchBarTintColor = nil; } } +- (void)accessibilityInvertColorsStatusDidChange +{ + // Refresh the theme only for "auto" + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (!theme || [theme isEqualToString:@"auto"]) + { + [self userInterfaceThemeDidChange]; + } +} + - (void)userInterfaceThemeDidChange { - // Retrieve the current selected theme ("light" if none). + // Retrieve the current selected theme ("light" if none. "auto" is used as default from iOS 11). NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + + if (!theme || [theme isEqualToString:@"auto"]) + { + theme = UIAccessibilityIsInvertColorsEnabled() ? @"dark" : @"light"; + } // Currently only 2 themes is supported if ([theme isEqualToString:@"dark"]) diff --git a/Riot/ViewController/SettingsViewController.m b/Riot/ViewController/SettingsViewController.m index 26c461620..b09e95ffb 100644 --- a/Riot/ViewController/SettingsViewController.m +++ b/Riot/ViewController/SettingsViewController.m @@ -1695,36 +1695,39 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } else if (row == USER_INTERFACE_THEME_INDEX) { - uiThemeCell = [tableView dequeueReusableCellWithIdentifier:[TableViewCellWithCheckBoxes defaultReuseIdentifier] forIndexPath:indexPath]; - - uiThemeCell.mainContainerLeadingConstraint.constant = uiThemeCell.separatorInset.left; - - uiThemeCell.checkBoxesNumber = 2; - - uiThemeCell.allowsMultipleSelection = NO; - uiThemeCell.delegate = self; - - NSArray *labels = uiThemeCell.labels; - UILabel *label; - label = labels[0]; - label.textColor = kRiotPrimaryTextColor; - label.text = NSLocalizedStringFromTable(@"settings_ui_light_theme", @"Vector", nil); - label = labels[1]; - label.textColor = kRiotPrimaryTextColor; - label.text = NSLocalizedStringFromTable(@"settings_ui_dark_theme", @"Vector", nil); - - NSString *selectedTheme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; - if (selectedTheme && [selectedTheme isEqualToString:@"dark"]) + cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId]; + if (!cell) { - [uiThemeCell setCheckBoxValue:YES atIndex:1]; + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId]; } - else + + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (!theme) { - // Consider the light theme by default. - [uiThemeCell setCheckBoxValue:YES atIndex:0]; + if (@available(iOS 11.0, *)) + { + // "auto" is used the default value from iOS 11 + theme = @"auto"; + } + else + { + // Use "light" for older version + theme = @"light"; + } } - - cell = uiThemeCell; + + theme = [NSString stringWithFormat:@"settings_ui_theme_%@", theme]; + NSString *i18nTheme = NSLocalizedStringFromTable(theme, + @"Vector", + nil); + + cell.textLabel.textColor = kRiotPrimaryTextColor; + + cell.textLabel.text = NSLocalizedStringFromTable(@"settings_ui_theme", @"Vector", nil); + cell.detailTextLabel.text = i18nTheme; + + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + cell.selectionStyle = UITableViewCellSelectionStyleDefault; } } else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX) @@ -2270,6 +2273,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); languagePickerViewController.delegate = self; [self pushViewController:languagePickerViewController]; } + else if (row == USER_INTERFACE_THEME_INDEX) + { + [self showThemePicker]; + } } else if (section == SETTINGS_SECTION_IGNORED_USERS_INDEX) { @@ -3430,6 +3437,87 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } } +- (void)showThemePicker +{ + __weak typeof(self) weakSelf = self; + + __block UIAlertAction *autoAction, *lightAction, *darkAction; + NSString *themePickerMessage; + + void (^actionBlock)(UIAlertAction *action) = ^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + NSString *newTheme; + if (action == autoAction) + { + newTheme = @"auto"; + } + else if (action == lightAction) + { + newTheme = @"light"; + } + else if (action == darkAction) + { + newTheme = @"dark"; + } + + NSString *theme = [[NSUserDefaults standardUserDefaults] stringForKey:@"userInterfaceTheme"]; + if (newTheme && ![newTheme isEqualToString:theme]) + { + // Clear fake Riot Avatars based on the previous theme. + [AvatarGenerator clear]; + + // The user wants to select this theme + [[NSUserDefaults standardUserDefaults] setObject:newTheme forKey:@"userInterfaceTheme"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + [self.tableView reloadData]; + } + } + }; + + if (@available(iOS 11.0, *)) + { + // Show "auto" only from iOS 11 + autoAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_auto", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + // Explain what is "auto" + themePickerMessage = NSLocalizedStringFromTable(@"settings_ui_theme_picker_message", @"Vector", nil); + } + + lightAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_light", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + darkAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_dark", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:actionBlock]; + + // Ask the user the kind of the call: voice or video? + UIAlertController *themePicker = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_picker_title", @"Vector", nil) + message:themePickerMessage + preferredStyle:UIAlertControllerStyleActionSheet]; + + if (autoAction) + { + [themePicker addAction:autoAction]; + } + [themePicker addAction:lightAction]; + [themePicker addAction:darkAction]; + + // Cancel button + [themePicker addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleDefault + handler:nil]]; + + [self presentViewController:themePicker animated:YES completion:nil]; +} + #pragma mark - MediaPickerViewController Delegate - (void)dismissMediaPicker @@ -3773,21 +3861,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); - (void)tableViewCellWithCheckBoxes:(TableViewCellWithCheckBoxes *)tableViewCellWithCheckBoxes didTapOnCheckBoxAtIndex:(NSUInteger)index { - if (tableViewCellWithCheckBoxes == uiThemeCell) - { - NSString *theme = (index == 0) ? @"light" : @"dark"; - BOOL isCurrentlySelected = [uiThemeCell checkBoxValueAtIndex:index]; - - if (!isCurrentlySelected) - { - // Clear fake Riot Avatars based on the previous theme. - [AvatarGenerator clear]; - - // The user wants to select this theme - [[NSUserDefaults standardUserDefaults] setObject:theme forKey:@"userInterfaceTheme"]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } - } } @end From d820406c0a7c7ef3a81f2a83fd7a972c741099fc Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 21 Sep 2017 15:36:09 +0200 Subject: [PATCH 2/3] Widget: code cleaning --- Riot/Utils/Widgets/Widget.m | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Riot/Utils/Widgets/Widget.m b/Riot/Utils/Widgets/Widget.m index 9fe5e12b1..93ddd73a5 100644 --- a/Riot/Utils/Widgets/Widget.m +++ b/Riot/Utils/Widgets/Widget.m @@ -41,22 +41,25 @@ MXJSONModelSetDictionary(_data, widgetEvent.content[@"data"]); // Format the url string with user data - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_user_id" withString:mxSession.myUser.userId]; - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_display_name" - withString:mxSession.myUser.displayname ? mxSession.myUser.displayname : mxSession.myUser.userId]; - _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_avatar_url" - withString:mxSession.myUser.avatarUrl ? mxSession.myUser.avatarUrl : @""]; + if (_url) + { + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_user_id" withString:mxSession.myUser.userId]; + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_display_name" + withString:mxSession.myUser.displayname ? mxSession.myUser.displayname : mxSession.myUser.userId]; + _url = [_url stringByReplacingOccurrencesOfString:@"$matrix_avatar_url" + withString:mxSession.myUser.avatarUrl ? mxSession.myUser.avatarUrl : @""]; - // And their scalar token - NSString *scalarToken = [[WidgetManager sharedManager] scalarTokenForMXSession:mxSession]; - if (scalarToken) - { - _url = [_url stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@", scalarToken]]; - } - else - { - // Some widget can live without scalar token (ex: Jitsi widget) - NSLog(@"[Widget] Note: There is no scalar token for %@", self); + // And their scalar token + NSString *scalarToken = [[WidgetManager sharedManager] scalarTokenForMXSession:mxSession]; + if (scalarToken) + { + _url = [_url stringByAppendingString:[NSString stringWithFormat:@"&scalar_token=%@", scalarToken]]; + } + else + { + // Some widget can live without scalar token (ex: Jitsi widget) + NSLog(@"[Widget] Note: There is no scalar token for %@", self); + } } } From e754c9f39451a6bb36b6019edd2215d0cf5c697f Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 21 Sep 2017 17:03:43 +0200 Subject: [PATCH 3/3] BF: [iOS11] "Smart [colors] Invert" renders badly in the app Take into account Giom's review --- Riot/ViewController/SettingsViewController.h | 3 +-- Riot/ViewController/SettingsViewController.m | 15 ++++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Riot/ViewController/SettingsViewController.h b/Riot/ViewController/SettingsViewController.h index 6713cb94d..6c9bff125 100644 --- a/Riot/ViewController/SettingsViewController.h +++ b/Riot/ViewController/SettingsViewController.h @@ -19,9 +19,8 @@ #import "DeviceView.h" #import "MediaPickerViewController.h" -#import "TableViewCellWithCheckBoxes.h" -@interface SettingsViewController : MXKTableViewController +@interface SettingsViewController : MXKTableViewController @end diff --git a/Riot/ViewController/SettingsViewController.m b/Riot/ViewController/SettingsViewController.m index b09e95ffb..72e42f0ec 100644 --- a/Riot/ViewController/SettingsViewController.m +++ b/Riot/ViewController/SettingsViewController.m @@ -205,9 +205,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); BOOL keepNewEmailEditing; BOOL keepNewPhoneNumberEditing; - // The user interface theme cell - TableViewCellWithCheckBoxes *uiThemeCell; - // The current pushed view controller UIViewController *pushedViewController; } @@ -250,7 +247,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]]; [self.tableView registerClass:MXKTableViewCellWithLabelAndMXKImageView.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndMXKImageView defaultReuseIdentifier]]; [self.tableView registerClass:TableViewCellWithPhoneNumberTextField.class forCellReuseIdentifier:[TableViewCellWithPhoneNumberTextField defaultReuseIdentifier]]; - [self.tableView registerClass:TableViewCellWithCheckBoxes.class forCellReuseIdentifier:[TableViewCellWithCheckBoxes defaultReuseIdentifier]]; // Enable self sizing cells self.tableView.rowHeight = UITableViewAutomaticDimension; @@ -3498,7 +3494,6 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); style:UIAlertActionStyleDefault handler:actionBlock]; - // Ask the user the kind of the call: voice or video? UIAlertController *themePicker = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_ui_theme_picker_title", @"Vector", nil) message:themePickerMessage preferredStyle:UIAlertControllerStyleActionSheet]; @@ -3515,6 +3510,10 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); style:UIAlertActionStyleDefault handler:nil]]; + UIView *fromCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:USER_INTERFACE_THEME_INDEX inSection:SETTINGS_SECTION_USER_INTERFACE_INDEX]]; + [themePicker popoverPresentationController].sourceView = fromCell; + [themePicker popoverPresentationController].sourceRect = fromCell.bounds; + [self presentViewController:themePicker animated:YES completion:nil]; } @@ -3857,10 +3856,4 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } } -#pragma mark - TableViewCellWithCheckBoxesDelegate - -- (void)tableViewCellWithCheckBoxes:(TableViewCellWithCheckBoxes *)tableViewCellWithCheckBoxes didTapOnCheckBoxAtIndex:(NSUInteger)index -{ -} - @end