fake_autocomplete_suggestion.mm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. // Copyright 2019 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 "ios/showcase/omnibox_popup/fake_autocomplete_suggestion.h"
  5. #import "ios/chrome/browser/net/crurl.h"
  6. #import "ios/chrome/browser/ui/omnibox/omnibox_suggestion_icon_util.h"
  7. #import "ios/chrome/browser/ui/omnibox/popup/simple_omnibox_icon.h"
  8. #import "url/gurl.h"
  9. #if !defined(__has_feature) || !__has_feature(objc_arc)
  10. #error "This file requires ARC support."
  11. #endif
  12. namespace {
  13. // Spacer attributed string for dividing parts of an autocomplete suggestion's
  14. // text and detail text.
  15. NSAttributedString* spacer() {
  16. return [[NSAttributedString alloc] initWithString:@" "];
  17. }
  18. // Standard attributed string for the text part of a suggestion.
  19. NSAttributedString* textString(NSString* text) {
  20. return [[NSAttributedString alloc]
  21. initWithString:text
  22. attributes:@{
  23. NSFontAttributeName : [UIFont systemFontOfSize:17],
  24. NSForegroundColorAttributeName : [UIColor blackColor],
  25. }];
  26. }
  27. // Standard attributed string for the detail part of a suggestion.
  28. NSAttributedString* detailTextString(NSString* detailText) {
  29. return [[NSAttributedString alloc]
  30. initWithString:detailText
  31. attributes:@{
  32. NSFontAttributeName : [UIFont systemFontOfSize:12],
  33. NSForegroundColorAttributeName : [UIColor colorWithWhite:0
  34. alpha:0.41],
  35. }];
  36. }
  37. // Main text for an autocomplete suggestion representing weather
  38. NSAttributedString* weatherText() {
  39. return [[NSAttributedString alloc]
  40. initWithString:@"weather"
  41. attributes:@{
  42. NSFontAttributeName : [UIFont systemFontOfSize:16],
  43. }];
  44. }
  45. // Detail text for an autocomplete suggestion representing weather
  46. NSAttributedString* weatherDetailText() {
  47. NSAttributedString* number = [[NSAttributedString alloc]
  48. initWithString:@"18"
  49. attributes:@{
  50. NSFontAttributeName : [UIFont systemFontOfSize:24],
  51. NSForegroundColorAttributeName : [UIColor grayColor],
  52. }];
  53. NSAttributedString* degreeSymbol = [[NSAttributedString alloc]
  54. initWithString:@"°C"
  55. attributes:@{
  56. NSFontAttributeName : [UIFont systemFontOfSize:12],
  57. NSBaselineOffsetAttributeName : @10.0f,
  58. NSForegroundColorAttributeName : [UIColor grayColor],
  59. }];
  60. NSAttributedString* date = [[NSAttributedString alloc]
  61. initWithString:@"ven."
  62. attributes:@{
  63. NSFontAttributeName : [UIFont systemFontOfSize:12],
  64. NSForegroundColorAttributeName : [UIColor grayColor],
  65. }];
  66. NSMutableAttributedString* answer =
  67. [[NSMutableAttributedString alloc] initWithAttributedString:number];
  68. [answer appendAttributedString:degreeSymbol];
  69. [answer appendAttributedString:spacer()];
  70. [answer appendAttributedString:date];
  71. return [answer copy];
  72. }
  73. // Main text for an autocomplete suggestion representing stock price
  74. NSAttributedString* stockText() {
  75. NSAttributedString* search = [[NSAttributedString alloc]
  76. initWithString:@"goog stock"
  77. attributes:@{
  78. NSFontAttributeName : [UIFont systemFontOfSize:16],
  79. }];
  80. NSAttributedString* priceSource = [[NSAttributedString alloc]
  81. initWithString:@"GOOG (NASDAQ), 13:18 UTC−4"
  82. attributes:@{
  83. NSFontAttributeName : [UIFont systemFontOfSize:12],
  84. NSForegroundColorAttributeName : [UIColor grayColor],
  85. }];
  86. NSMutableAttributedString* answer =
  87. [[NSMutableAttributedString alloc] initWithAttributedString:search];
  88. [answer appendAttributedString:spacer()];
  89. [answer appendAttributedString:priceSource];
  90. return [answer copy];
  91. }
  92. // Detail text for an autocomplete suggestion representing stock price
  93. NSAttributedString* stockDetailText() {
  94. NSAttributedString* price = [[NSAttributedString alloc]
  95. initWithString:@"1 209,29"
  96. attributes:@{
  97. NSFontAttributeName : [UIFont systemFontOfSize:24],
  98. NSForegroundColorAttributeName : [UIColor grayColor],
  99. }];
  100. NSAttributedString* priceChange = [[NSAttributedString alloc]
  101. initWithString:@"-22,25 (-1,81%)"
  102. attributes:@{
  103. NSFontAttributeName : [UIFont systemFontOfSize:16],
  104. NSForegroundColorAttributeName : [UIColor colorWithRed:197 / 255.0
  105. green:57 / 255.0
  106. blue:41 / 255.0
  107. alpha:1.0],
  108. }];
  109. NSMutableAttributedString* answer =
  110. [[NSMutableAttributedString alloc] initWithAttributedString:price];
  111. [answer appendAttributedString:spacer()];
  112. [answer appendAttributedString:priceChange];
  113. return [answer copy];
  114. }
  115. // Main text for an autocomplete suggestion representing a word definition
  116. NSAttributedString* definitionText() {
  117. NSAttributedString* searchText = [[NSAttributedString alloc]
  118. initWithString:@"define government"
  119. attributes:@{
  120. NSFontAttributeName : [UIFont systemFontOfSize:16],
  121. }];
  122. NSAttributedString* pronunciation = [[NSAttributedString alloc]
  123. initWithString:@"• /ˈɡʌv(ə)nˌm(ə)nt/"
  124. attributes:@{
  125. NSFontAttributeName : [UIFont systemFontOfSize:14],
  126. NSForegroundColorAttributeName : [UIColor grayColor],
  127. }];
  128. NSMutableAttributedString* answer =
  129. [[NSMutableAttributedString alloc] initWithAttributedString:searchText];
  130. [answer appendAttributedString:spacer()];
  131. [answer appendAttributedString:pronunciation];
  132. return [answer copy];
  133. }
  134. // Detail text for an autocomplete suggestion representing a word definition
  135. NSAttributedString* definitionDetailText() {
  136. return [[NSAttributedString alloc]
  137. initWithString:@"the group of people with the authority to govern a "
  138. @"country or state; a particular ministry in office. "
  139. @"Let's expand this definition to get to three lines also."
  140. attributes:@{
  141. NSFontAttributeName : [UIFont systemFontOfSize:14],
  142. NSForegroundColorAttributeName : [UIColor grayColor],
  143. }];
  144. }
  145. NSAttributedString* sunriseText() {
  146. return [[NSAttributedString alloc]
  147. initWithString:@"sunrise in paris"
  148. attributes:@{
  149. NSFontAttributeName : [UIFont systemFontOfSize:16],
  150. }];
  151. }
  152. NSAttributedString* sunriseDetailText() {
  153. return [[NSAttributedString alloc]
  154. initWithString:@"06:35"
  155. attributes:@{
  156. NSFontAttributeName : [UIFont systemFontOfSize:24],
  157. NSForegroundColorAttributeName : [UIColor grayColor],
  158. }];
  159. }
  160. NSAttributedString* knowledgeText() {
  161. return [[NSAttributedString alloc]
  162. initWithString:@"how high is mount everest"
  163. attributes:@{
  164. NSFontAttributeName : [UIFont systemFontOfSize:16],
  165. }];
  166. }
  167. NSAttributedString* knowledgeDetailText() {
  168. return [[NSAttributedString alloc]
  169. initWithString:@"8 848 m"
  170. attributes:@{
  171. NSFontAttributeName : [UIFont systemFontOfSize:24],
  172. NSForegroundColorAttributeName : [UIColor grayColor],
  173. }];
  174. }
  175. NSAttributedString* sportsText() {
  176. return [[NSAttributedString alloc]
  177. initWithString:@"boston celtics"
  178. attributes:@{
  179. NSFontAttributeName : [UIFont systemFontOfSize:16],
  180. }];
  181. }
  182. NSAttributedString* sportsDetailText() {
  183. return [[NSAttributedString alloc]
  184. initWithString:@" contre Pacers 30 mars à 00:00 UTC+1"
  185. attributes:@{
  186. NSFontAttributeName : [UIFont systemFontOfSize:14],
  187. NSForegroundColorAttributeName : [UIColor grayColor],
  188. }];
  189. }
  190. NSAttributedString* whenIsText() {
  191. return [[NSAttributedString alloc]
  192. initWithString:@"when is bastille day"
  193. attributes:@{
  194. NSFontAttributeName : [UIFont systemFontOfSize:16],
  195. }];
  196. }
  197. NSAttributedString* whenIsDetailText() {
  198. return [[NSAttributedString alloc]
  199. initWithString:@"dimanche 14 juillet 2019"
  200. attributes:@{
  201. NSFontAttributeName : [UIFont systemFontOfSize:20],
  202. NSForegroundColorAttributeName : [UIColor grayColor],
  203. }];
  204. }
  205. NSAttributedString* currencyText() {
  206. return [[NSAttributedString alloc]
  207. initWithString:@"100 usd"
  208. attributes:@{
  209. NSFontAttributeName : [UIFont systemFontOfSize:16],
  210. }];
  211. }
  212. NSAttributedString* currencyDetailText() {
  213. return [[NSAttributedString alloc]
  214. initWithString:@"100 Dollar américain = 89.01 Euro"
  215. attributes:@{
  216. NSFontAttributeName : [UIFont systemFontOfSize:20],
  217. NSForegroundColorAttributeName : [UIColor grayColor],
  218. }];
  219. }
  220. NSAttributedString* translateText() {
  221. return [[NSAttributedString alloc]
  222. initWithString:@"bonjour in chinese"
  223. attributes:@{
  224. NSFontAttributeName : [UIFont systemFontOfSize:16],
  225. }];
  226. }
  227. NSAttributedString* translateDetailText() {
  228. return [[NSAttributedString alloc]
  229. initWithString:@"你好 (Chinois (simplifié))"
  230. attributes:@{
  231. NSFontAttributeName : [UIFont systemFontOfSize:20],
  232. NSForegroundColorAttributeName : [UIColor grayColor],
  233. }];
  234. }
  235. NSAttributedString* calculatorText() {
  236. return [[NSAttributedString alloc]
  237. initWithString:@"= 3.46410162"
  238. attributes:@{
  239. NSFontAttributeName : [UIFont systemFontOfSize:17
  240. weight:UIFontWeightMedium],
  241. NSForegroundColorAttributeName : [UIColor blackColor],
  242. }];
  243. }
  244. } // namespace
  245. @interface FakeAutocompleteSuggestion ()
  246. @property(nonatomic, copy, readwrite) NSString* commonPrefix;
  247. @end
  248. @implementation FakeAutocompleteSuggestion
  249. @synthesize isClipboardMatch = _isClipboardMatch;
  250. - (instancetype)init {
  251. self = [super init];
  252. if (self) {
  253. _isURL = YES;
  254. _text = [[NSAttributedString alloc] initWithString:@""];
  255. _detailText = [[NSAttributedString alloc] initWithString:@""];
  256. _numberOfLines = 1;
  257. _suggestionTypeIcon =
  258. [[UIImage imageNamed:@"omnibox_completion_default_favicon"]
  259. imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  260. _icon = [[SimpleOmniboxIcon alloc] init];
  261. _isClipboardMatch = NO;
  262. }
  263. return self;
  264. }
  265. // In the new popup, this field is not used. Instead, the icon field is used.
  266. - (GURL)imageURL {
  267. return GURL();
  268. }
  269. // In the new popup, this field is not used. Instead, the icon field is used.
  270. - (GURL)faviconPageURL {
  271. return GURL();
  272. }
  273. // In the new popup, this field is not used. Instead, the icon field, which
  274. // always has an image, is used.
  275. - (BOOL)hasImage {
  276. return self.imageURL.is_valid();
  277. }
  278. + (instancetype)simpleSuggestion {
  279. FakeAutocompleteSuggestion* suggestion =
  280. [[FakeAutocompleteSuggestion alloc] init];
  281. suggestion.text = textString(@"Simple suggestion");
  282. suggestion.icon = [[SimpleOmniboxIcon alloc]
  283. initWithIconType:OmniboxIconTypeSuggestionIcon
  284. suggestionIconType:SEARCH
  285. isAnswer:NO
  286. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  287. return suggestion;
  288. }
  289. + (instancetype)suggestionWithDetail {
  290. FakeAutocompleteSuggestion* suggestion =
  291. [[FakeAutocompleteSuggestion alloc] init];
  292. suggestion.text = textString(@"Suggestion with detail");
  293. suggestion.detailText = detailTextString(@"Detail");
  294. suggestion.icon = [[SimpleOmniboxIcon alloc]
  295. initWithIconType:OmniboxIconTypeSuggestionIcon
  296. suggestionIconType:SEARCH
  297. isAnswer:NO
  298. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  299. return suggestion;
  300. }
  301. + (instancetype)clippingSuggestion {
  302. FakeAutocompleteSuggestion* suggestion =
  303. [[FakeAutocompleteSuggestion alloc] init];
  304. suggestion.text =
  305. textString(@"Suggestion with text that clips because it is very long "
  306. @"and extends off the right end of the screen");
  307. suggestion.detailText = detailTextString(
  308. @"Detail about the suggestion that also clips because it is too long "
  309. @"for the screen and extends off of the right edge.");
  310. suggestion.icon = [[SimpleOmniboxIcon alloc]
  311. initWithIconType:OmniboxIconTypeSuggestionIcon
  312. suggestionIconType:SEARCH
  313. isAnswer:NO
  314. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  315. return suggestion;
  316. }
  317. + (instancetype)appendableSuggestion {
  318. FakeAutocompleteSuggestion* suggestion =
  319. [[FakeAutocompleteSuggestion alloc] init];
  320. suggestion.text = textString(@"Appendable suggestion");
  321. suggestion.isAppendable = true;
  322. return suggestion;
  323. }
  324. + (instancetype)otherTabSuggestion {
  325. FakeAutocompleteSuggestion* suggestion =
  326. [[FakeAutocompleteSuggestion alloc] init];
  327. suggestion.text = textString(@"Other tab suggestion");
  328. suggestion.isTabMatch = true;
  329. return suggestion;
  330. }
  331. + (instancetype)deletableSuggestion {
  332. FakeAutocompleteSuggestion* suggestion =
  333. [[FakeAutocompleteSuggestion alloc] init];
  334. suggestion.text = textString(@"Deletable suggestion");
  335. suggestion.supportsDeletion = YES;
  336. return suggestion;
  337. }
  338. + (instancetype)weatherSuggestion {
  339. FakeAutocompleteSuggestion* suggestion =
  340. [[FakeAutocompleteSuggestion alloc] init];
  341. suggestion.text = weatherText();
  342. suggestion.hasAnswer = YES;
  343. suggestion.detailText = weatherDetailText();
  344. // The image currently doesn't display because there is no fake
  345. // Image Retriever, but leaving this here in case this is ever necessary.
  346. suggestion.icon = [[SimpleOmniboxIcon alloc]
  347. initWithIconType:OmniboxIconTypeImage
  348. suggestionIconType:DEFAULT_FAVICON
  349. isAnswer:NO
  350. imageURL:[[CrURL alloc]
  351. initWithGURL:GURL("https://ssl.gstatic.com/onebox/"
  352. "weather/128/sunny.png")]];
  353. return suggestion;
  354. }
  355. + (instancetype)stockSuggestion {
  356. FakeAutocompleteSuggestion* suggestion =
  357. [[FakeAutocompleteSuggestion alloc] init];
  358. suggestion.text = stockText();
  359. suggestion.hasAnswer = YES;
  360. suggestion.detailText = stockDetailText();
  361. suggestion.icon = [[SimpleOmniboxIcon alloc]
  362. initWithIconType:OmniboxIconTypeSuggestionIcon
  363. suggestionIconType:STOCK
  364. isAnswer:NO
  365. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  366. return suggestion;
  367. }
  368. + (instancetype)definitionSuggestion {
  369. FakeAutocompleteSuggestion* suggestion =
  370. [[FakeAutocompleteSuggestion alloc] init];
  371. suggestion.text = definitionText();
  372. suggestion.numberOfLines = 3;
  373. suggestion.hasAnswer = YES;
  374. suggestion.detailText = definitionDetailText();
  375. suggestion.icon = [[SimpleOmniboxIcon alloc]
  376. initWithIconType:OmniboxIconTypeSuggestionIcon
  377. suggestionIconType:DICTIONARY
  378. isAnswer:NO
  379. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  380. return suggestion;
  381. }
  382. + (instancetype)sunriseSuggestion {
  383. FakeAutocompleteSuggestion* suggestion =
  384. [[FakeAutocompleteSuggestion alloc] init];
  385. suggestion.text = sunriseText();
  386. suggestion.hasAnswer = YES;
  387. suggestion.detailText = sunriseDetailText();
  388. suggestion.icon = [[SimpleOmniboxIcon alloc]
  389. initWithIconType:OmniboxIconTypeSuggestionIcon
  390. suggestionIconType:SUNRISE
  391. isAnswer:NO
  392. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  393. return suggestion;
  394. }
  395. + (instancetype)knowledgeSuggestion {
  396. FakeAutocompleteSuggestion* suggestion =
  397. [[FakeAutocompleteSuggestion alloc] init];
  398. suggestion.text = knowledgeText();
  399. suggestion.hasAnswer = YES;
  400. suggestion.detailText = knowledgeDetailText();
  401. suggestion.icon = [[SimpleOmniboxIcon alloc]
  402. initWithIconType:OmniboxIconTypeSuggestionIcon
  403. suggestionIconType:FALLBACK_ANSWER
  404. isAnswer:NO
  405. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  406. return suggestion;
  407. }
  408. + (instancetype)sportsSuggestion {
  409. FakeAutocompleteSuggestion* suggestion =
  410. [[FakeAutocompleteSuggestion alloc] init];
  411. suggestion.text = sportsText();
  412. suggestion.hasAnswer = YES;
  413. suggestion.detailText = sportsDetailText();
  414. suggestion.icon = [[SimpleOmniboxIcon alloc]
  415. initWithIconType:OmniboxIconTypeSuggestionIcon
  416. suggestionIconType:FALLBACK_ANSWER
  417. isAnswer:NO
  418. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  419. return suggestion;
  420. }
  421. + (instancetype)whenIsSuggestion {
  422. FakeAutocompleteSuggestion* suggestion =
  423. [[FakeAutocompleteSuggestion alloc] init];
  424. suggestion.text = whenIsText();
  425. suggestion.hasAnswer = YES;
  426. suggestion.detailText = whenIsDetailText();
  427. suggestion.icon = [[SimpleOmniboxIcon alloc]
  428. initWithIconType:OmniboxIconTypeSuggestionIcon
  429. suggestionIconType:WHEN_IS
  430. isAnswer:NO
  431. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  432. return suggestion;
  433. }
  434. + (instancetype)currencySuggestion {
  435. FakeAutocompleteSuggestion* suggestion =
  436. [[FakeAutocompleteSuggestion alloc] init];
  437. suggestion.text = currencyText();
  438. suggestion.hasAnswer = YES;
  439. suggestion.detailText = currencyDetailText();
  440. suggestion.icon = [[SimpleOmniboxIcon alloc]
  441. initWithIconType:OmniboxIconTypeSuggestionIcon
  442. suggestionIconType:CONVERSION
  443. isAnswer:NO
  444. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  445. return suggestion;
  446. }
  447. + (instancetype)translateSuggestion {
  448. FakeAutocompleteSuggestion* suggestion =
  449. [[FakeAutocompleteSuggestion alloc] init];
  450. suggestion.text = translateText();
  451. suggestion.hasAnswer = YES;
  452. suggestion.detailText = translateDetailText();
  453. suggestion.icon = [[SimpleOmniboxIcon alloc]
  454. initWithIconType:OmniboxIconTypeSuggestionIcon
  455. suggestionIconType:TRANSLATION
  456. isAnswer:NO
  457. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  458. return suggestion;
  459. }
  460. + (instancetype)calculatorSuggestion {
  461. FakeAutocompleteSuggestion* suggestion =
  462. [[FakeAutocompleteSuggestion alloc] init];
  463. suggestion.text = calculatorText();
  464. suggestion.icon = [[SimpleOmniboxIcon alloc]
  465. initWithIconType:OmniboxIconTypeSuggestionIcon
  466. suggestionIconType:CALCULATOR
  467. isAnswer:NO
  468. imageURL:[[CrURL alloc] initWithGURL:GURL()]];
  469. return suggestion;
  470. }
  471. + (instancetype)richEntitySuggestion {
  472. FakeAutocompleteSuggestion* suggestion =
  473. [[FakeAutocompleteSuggestion alloc] init];
  474. suggestion.text = textString(@"Avengers : Endgame");
  475. suggestion.detailText = detailTextString(@"Film (2019)");
  476. // The image currently doesn't display because there is no fake
  477. // Image Retriever, but leaving this here in case this is ever necessary.
  478. GURL imageURL = GURL("https://encrypted-tbn0.gstatic.com/"
  479. "images?q=tbn:ANd9GcRl35jshKCRWt76yUSKh5r0_"
  480. "BRbWuSU1uZOCGnzq95nJ8yXUg913LciCgz-s3reyfACsrAAYg");
  481. suggestion.icon = [[SimpleOmniboxIcon alloc]
  482. initWithIconType:OmniboxIconTypeImage
  483. suggestionIconType:SEARCH
  484. isAnswer:NO
  485. imageURL:[[CrURL alloc] initWithGURL:imageURL]];
  486. return suggestion;
  487. }
  488. @end