host_collection_view_cell.mm 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import <UIKit/UIKit.h>
  5. #import <MaterialComponents/MaterialTypography.h>
  6. #include "base/strings/sys_string_conversions.h"
  7. #include "remoting/base/string_resources.h"
  8. #import "remoting/ios/app/host_collection_view_cell.h"
  9. #import "remoting/ios/app/remoting_theme.h"
  10. #import "remoting/ios/app/view_utils.h"
  11. #import "remoting/ios/domain/host_info.h"
  12. #include "ui/base/l10n/l10n_util.h"
  13. #if !defined(__has_feature) || !__has_feature(objc_arc)
  14. #error "This file requires ARC support."
  15. #endif
  16. static const CGFloat kLinePadding = 2.f;
  17. static const CGFloat kHostCardIconInset = 10.f;
  18. static const CGFloat kHostCardPadding = 4.f;
  19. static const CGFloat kHostCardIconSize = 45.f;
  20. static const CGFloat kTitleOpacity = 0.87f;
  21. static const CGFloat kCaptionOpacity = 0.54f;
  22. static NSString* const kSuccessExitOfflineReason = @"SUCCESS_EXIT";
  23. // Maps an offline reason enum string to the l10n ID used to retrieve the
  24. // localized message.
  25. static NSDictionary<NSString*, NSNumber*>* const kOfflineReasonL10nId = @{
  26. @"INITIALIZATION_FAILED" : @(IDS_OFFLINE_REASON_INITIALIZATION_FAILED),
  27. @"INVALID_HOST_CONFIGURATION" :
  28. @(IDS_OFFLINE_REASON_INVALID_HOST_CONFIGURATION),
  29. @"INVALID_HOST_ID" : @(IDS_OFFLINE_REASON_INVALID_HOST_ID),
  30. @"INVALID_OAUTH_CREDENTIALS" :
  31. @(IDS_OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS),
  32. @"INVALID_HOST_DOMAIN" : @(IDS_OFFLINE_REASON_INVALID_HOST_DOMAIN),
  33. @"LOGIN_SCREEN_NOT_SUPPORTED" :
  34. @(IDS_OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED),
  35. @"POLICY_READ_ERROR" : @(IDS_OFFLINE_REASON_POLICY_READ_ERROR),
  36. @"POLICY_CHANGE_REQUIRES_RESTART" :
  37. @(IDS_OFFLINE_REASON_POLICY_CHANGE_REQUIRES_RESTART),
  38. @"USERNAME_MISMATCH" : @(IDS_OFFLINE_REASON_USERNAME_MISMATCH),
  39. @"X_SERVER_RETRIES_EXCEEDED" :
  40. @(IDS_OFFLINE_REASON_X_SERVER_RETRIES_EXCEEDED),
  41. @"SESSION_RETRIES_EXCEEDED" : @(IDS_OFFLINE_REASON_SESSION_RETRIES_EXCEEDED),
  42. @"HOST_RETRIES_EXCEEDED" : @(IDS_OFFLINE_REASON_HOST_RETRIES_EXCEEDED),
  43. };
  44. @interface HostCollectionViewCell () {
  45. UIImageView* _imageView;
  46. UILabel* _statusLabel;
  47. UILabel* _titleLabel;
  48. UIView* _labelView;
  49. }
  50. @end
  51. //
  52. // This is the implementation of the info card for a host's status shown in
  53. // the host list. This will also be the selection for which host to connect
  54. // to and other managements actions for a host in this list.
  55. //
  56. @implementation HostCollectionViewCell
  57. @synthesize hostInfo = _hostInfo;
  58. - (id)initWithFrame:(CGRect)frame {
  59. self = [super initWithFrame:frame];
  60. if (self) {
  61. self.backgroundColor = [UIColor clearColor];
  62. [self commonInit];
  63. }
  64. return self;
  65. }
  66. - (void)commonInit {
  67. self.isAccessibilityElement = YES;
  68. _imageView = [[UIImageView alloc] init];
  69. _imageView.translatesAutoresizingMaskIntoConstraints = NO;
  70. _imageView.contentMode = UIViewContentModeCenter;
  71. _imageView.backgroundColor = RemotingTheme.hostOfflineColor;
  72. _imageView.layer.cornerRadius = kHostCardIconSize / 2.f;
  73. _imageView.layer.masksToBounds = YES;
  74. [self.contentView addSubview:_imageView];
  75. // Holds both of the labels.
  76. _labelView = [[UIView alloc] init];
  77. _labelView.translatesAutoresizingMaskIntoConstraints = NO;
  78. [self.contentView addSubview:_labelView];
  79. _titleLabel = [[UILabel alloc] init];
  80. _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  81. UIFont* subheadFont = MDCTypography.subheadFont;
  82. UIFontDescriptor* subheadFontDescriptor = [subheadFont.fontDescriptor
  83. fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
  84. subheadFontDescriptor = subheadFontDescriptor ?: subheadFont.fontDescriptor;
  85. UIFont* boldSubheadFont = [UIFont fontWithDescriptor:subheadFontDescriptor
  86. size:subheadFont.pointSize];
  87. _titleLabel.font = boldSubheadFont;
  88. _titleLabel.alpha = kTitleOpacity;
  89. _titleLabel.textColor = RemotingTheme.hostCellTitleColor;
  90. [_labelView addSubview:_titleLabel];
  91. _statusLabel = [[UILabel alloc] init];
  92. _statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
  93. _statusLabel.font = MDCTypography.captionFont;
  94. _statusLabel.alpha = kCaptionOpacity;
  95. _statusLabel.textColor = RemotingTheme.hostCellStatusTextColor;
  96. [_labelView addSubview:_statusLabel];
  97. UILayoutGuide* safeAreaLayoutGuide =
  98. remoting::SafeAreaLayoutGuideForView(self);
  99. // Constraints
  100. NSArray* constraints = @[
  101. // +------------+---------------+
  102. // | +--------+ | |
  103. // | | | | [Host Name] |
  104. // | | Icon | | - - - - - - - | <- Center Y
  105. // | | | | [Host Status] |
  106. // | +---^----+ | |
  107. // +-----|------+-------^-------+
  108. // | |
  109. // Image View Label View
  110. [[_imageView leadingAnchor]
  111. constraintEqualToAnchor:safeAreaLayoutGuide.leadingAnchor
  112. constant:kHostCardIconInset],
  113. [[_imageView centerYAnchor]
  114. constraintEqualToAnchor:safeAreaLayoutGuide.centerYAnchor],
  115. [[_imageView widthAnchor] constraintEqualToConstant:kHostCardIconSize],
  116. [[_imageView heightAnchor] constraintEqualToConstant:kHostCardIconSize],
  117. [[_labelView leadingAnchor]
  118. constraintEqualToAnchor:[_imageView trailingAnchor]
  119. constant:kHostCardIconInset],
  120. [[_labelView trailingAnchor]
  121. constraintEqualToAnchor:safeAreaLayoutGuide.trailingAnchor
  122. constant:-kHostCardPadding / 2.f],
  123. [[_labelView topAnchor]
  124. constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor],
  125. [[_labelView bottomAnchor]
  126. constraintEqualToAnchor:safeAreaLayoutGuide.bottomAnchor],
  127. // Put titleLabel and statusLabel symmetrically around centerY.
  128. [[_titleLabel leadingAnchor]
  129. constraintEqualToAnchor:[_labelView leadingAnchor]],
  130. [[_titleLabel trailingAnchor]
  131. constraintEqualToAnchor:[_labelView trailingAnchor]],
  132. [[_titleLabel bottomAnchor]
  133. constraintEqualToAnchor:[_labelView centerYAnchor]],
  134. [[_statusLabel leadingAnchor]
  135. constraintEqualToAnchor:[_labelView leadingAnchor]],
  136. [[_statusLabel trailingAnchor]
  137. constraintEqualToAnchor:[_labelView trailingAnchor]],
  138. [[_statusLabel topAnchor] constraintEqualToAnchor:[_labelView centerYAnchor]
  139. constant:kLinePadding],
  140. ];
  141. [NSLayoutConstraint activateConstraints:constraints];
  142. }
  143. #pragma mark - HostCollectionViewCell Public
  144. - (void)populateContentWithHostInfo:(HostInfo*)hostInfo {
  145. _hostInfo = hostInfo;
  146. _titleLabel.text = _hostInfo.hostName;
  147. _imageView.image = RemotingTheme.desktopIcon;
  148. if (_hostInfo.isOnline) {
  149. _imageView.backgroundColor = RemotingTheme.hostOnlineColor;
  150. _statusLabel.text = l10n_util::GetNSString(IDS_HOST_ONLINE_SUBTITLE);
  151. } else {
  152. NSString* statusText =
  153. hostInfo.updatedTime
  154. ? l10n_util::GetNSStringF(
  155. IDS_LAST_ONLINE_SUBTITLE,
  156. base::SysNSStringToUTF16(hostInfo.updatedTime))
  157. : l10n_util::GetNSString(IDS_HOST_OFFLINE_SUBTITLE);
  158. NSString* localizedOfflineReason = nil;
  159. if (hostInfo.offlineReason.length > 0 &&
  160. ![hostInfo.offlineReason isEqualToString:kSuccessExitOfflineReason]) {
  161. NSNumber* offlineReasonId = kOfflineReasonL10nId[hostInfo.offlineReason];
  162. if (offlineReasonId) {
  163. localizedOfflineReason =
  164. l10n_util::GetNSString(offlineReasonId.intValue);
  165. } else {
  166. localizedOfflineReason = l10n_util::GetNSStringF(
  167. IDS_OFFLINE_REASON_UNKNOWN,
  168. base::SysNSStringToUTF16(hostInfo.offlineReason));
  169. }
  170. }
  171. if (localizedOfflineReason) {
  172. _imageView.backgroundColor = RemotingTheme.hostWarningColor;
  173. _statusLabel.text = [NSString
  174. stringWithFormat:@"%@ %@", localizedOfflineReason, statusText];
  175. } else {
  176. _imageView.backgroundColor = RemotingTheme.hostOfflineColor;
  177. _statusLabel.text = statusText;
  178. }
  179. }
  180. self.accessibilityLabel = [NSString
  181. stringWithFormat:@"%@\n%@", _titleLabel.text, _statusLabel.text];
  182. }
  183. #pragma mark - UICollectionReusableView
  184. - (void)prepareForReuse {
  185. [super prepareForReuse];
  186. _hostInfo = nil;
  187. _statusLabel.text = nil;
  188. _titleLabel.text = nil;
  189. self.accessibilityLabel = nil;
  190. }
  191. @end