OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/widget_extension/widget_view_controller.h" | 5 #import "ios/chrome/widget_extension/widget_view_controller.h" |
6 | 6 |
7 #import <NotificationCenter/NotificationCenter.h> | 7 #import <NotificationCenter/NotificationCenter.h> |
8 | 8 |
| 9 #include "base/ios/ios_util.h" |
9 #include "base/mac/foundation_util.h" | 10 #include "base/mac/foundation_util.h" |
10 #include "base/strings/sys_string_conversions.h" | 11 #include "base/strings/sys_string_conversions.h" |
| 12 #include "components/open_from_clipboard/clipboard_recent_content_impl_ios.h" |
11 #include "ios/chrome/common/app_group/app_group_constants.h" | 13 #include "ios/chrome/common/app_group/app_group_constants.h" |
12 #include "ios/chrome/common/x_callback_url.h" | |
13 #import "ios/chrome/widget_extension/widget_view.h" | 14 #import "ios/chrome/widget_extension/widget_view.h" |
14 #import "net/base/mac/url_conversions.h" | |
15 #include "url/gurl.h" | |
16 | 15 |
17 #if !defined(__has_feature) || !__has_feature(objc_arc) | 16 #if !defined(__has_feature) || !__has_feature(objc_arc) |
18 #error "This file requires ARC support." | 17 #error "This file requires ARC support." |
19 #endif | 18 #endif |
20 | 19 |
| 20 namespace { |
| 21 // Using GURL in the extension is not wanted as it includes ICU which makes the |
| 22 // extension binary much larger; therefore, ios/chrome/common/x_callback_url.h |
| 23 // cannot be used. This class makes a very basic use of x-callback-url, so no |
| 24 // full implementation is required. |
| 25 NSString* const kXCallbackURLHost = @"x-callback-url"; |
| 26 } // namespace |
| 27 |
21 @interface WidgetViewController ()<WidgetViewActionTarget> | 28 @interface WidgetViewController ()<WidgetViewActionTarget> |
22 @property(nonatomic, weak) WidgetView* widgetView; | 29 @property(nonatomic, weak) WidgetView* widgetView; |
| 30 @property(nonatomic, strong) NSURL* copiedURL; |
| 31 @property(nonatomic, strong) |
| 32 ClipboardRecentContentImplIOS* clipboardRecentContent; |
| 33 |
| 34 // Updates the widget with latest data from the clipboard. Returns whether any |
| 35 // visual updates occured. |
| 36 - (BOOL)updateWidget; |
| 37 // Opens the main application with the given |command|. |
| 38 - (void)openAppWithCommand:(NSString*)command; |
| 39 // Opens the main application with the given |command| and |parameter|. |
| 40 - (void)openAppWithCommand:(NSString*)command parameter:(NSString*)parameter; |
| 41 // Returns the dictionary of commands to pass via user defaults to open the main |
| 42 // application for a given |command| and |parameter|. |
| 43 + (NSDictionary*)dictForCommand:(NSString*)command |
| 44 parameter:(NSString*)parameter; |
| 45 |
23 @end | 46 @end |
24 | 47 |
25 @implementation WidgetViewController | 48 @implementation WidgetViewController |
26 | 49 |
27 @synthesize widgetView = _widgetView; | 50 @synthesize widgetView = _widgetView; |
| 51 @synthesize copiedURL = _copiedURL; |
| 52 @synthesize clipboardRecentContent = _clipboardRecentContent; |
| 53 |
| 54 - (instancetype)init { |
| 55 self = [super init]; |
| 56 if (self) { |
| 57 _clipboardRecentContent = [[ClipboardRecentContentImplIOS alloc] |
| 58 initWithAuthorizedSchemes:[NSSet setWithObjects:@"http", @"https", nil] |
| 59 userDefaults:app_group::GetGroupUserDefaults() |
| 60 delegate:nil]; |
| 61 } |
| 62 return self; |
| 63 } |
28 | 64 |
29 #pragma mark - UIViewController | 65 #pragma mark - UIViewController |
30 | 66 |
31 - (void)viewDidLoad { | 67 - (void)viewDidLoad { |
32 [super viewDidLoad]; | 68 [super viewDidLoad]; |
33 | 69 |
34 // A local variable is necessary here as the property is declared weak and the | 70 // A local variable is necessary here as the property is declared weak and the |
35 // object would be deallocated before being retained by the addSubview call. | 71 // object would be deallocated before being retained by the addSubview call. |
36 WidgetView* widgetView = [[WidgetView alloc] initWithActionTarget:self]; | 72 WidgetView* widgetView = [[WidgetView alloc] initWithActionTarget:self]; |
37 self.widgetView = widgetView; | 73 self.widgetView = widgetView; |
38 [self.view addSubview:self.widgetView]; | 74 [self.view addSubview:self.widgetView]; |
39 | 75 |
40 [self.widgetView setTranslatesAutoresizingMaskIntoConstraints:NO]; | 76 if (base::ios::IsRunningOnIOS10OrLater()) { |
| 77 self.extensionContext.widgetLargestAvailableDisplayMode = |
| 78 NCWidgetDisplayModeExpanded; |
| 79 } |
| 80 |
| 81 self.widgetView.translatesAutoresizingMaskIntoConstraints = NO; |
| 82 |
| 83 NSLayoutConstraint* heightAnchor = [self.widgetView.heightAnchor |
| 84 constraintEqualToAnchor:self.view.heightAnchor]; |
| 85 heightAnchor.priority = 900; |
| 86 |
41 [NSLayoutConstraint activateConstraints:@[ | 87 [NSLayoutConstraint activateConstraints:@[ |
42 [self.widgetView.leadingAnchor | 88 [self.widgetView.leadingAnchor |
43 constraintEqualToAnchor:[self.view leadingAnchor]], | 89 constraintEqualToAnchor:self.view.leadingAnchor], |
| 90 [self.widgetView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor], |
44 [self.widgetView.trailingAnchor | 91 [self.widgetView.trailingAnchor |
45 constraintEqualToAnchor:[self.view trailingAnchor]], | 92 constraintEqualToAnchor:self.view.trailingAnchor], |
46 [self.widgetView.heightAnchor | 93 heightAnchor, |
47 constraintEqualToAnchor:[self.view heightAnchor]], | 94 [self.widgetView.topAnchor constraintEqualToAnchor:self.view.topAnchor], |
48 [self.widgetView.widthAnchor | |
49 constraintEqualToAnchor:[self.view widthAnchor]] | |
50 ]]; | 95 ]]; |
51 } | 96 } |
52 | 97 |
53 - (void)openApp:(id)sender { | 98 - (void)viewWillAppear:(BOOL)animated { |
| 99 [super viewWillAppear:animated]; |
| 100 [self updateWidget]; |
| 101 } |
| 102 |
| 103 - (void)widgetPerformUpdateWithCompletionHandler: |
| 104 (void (^)(NCUpdateResult))completionHandler { |
| 105 completionHandler([self updateWidget] ? NCUpdateResultNewData |
| 106 : NCUpdateResultNoData); |
| 107 } |
| 108 |
| 109 - (BOOL)updateWidget { |
| 110 NSURL* url = [_clipboardRecentContent recentURLFromClipboard]; |
| 111 |
| 112 if (![url isEqual:self.copiedURL]) { |
| 113 self.copiedURL = url; |
| 114 [self.widgetView updateCopiedURL:self.copiedURL.absoluteString]; |
| 115 return YES; |
| 116 } |
| 117 return NO; |
| 118 } |
| 119 |
| 120 #pragma mark - NCWidgetProviding |
| 121 |
| 122 - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode |
| 123 withMaximumSize:(CGSize)maxSize { |
| 124 CGSize fittingSize = [self.widgetView |
| 125 systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; |
| 126 if (fittingSize.height > maxSize.height) { |
| 127 self.preferredContentSize = maxSize; |
| 128 } else { |
| 129 self.preferredContentSize = fittingSize; |
| 130 } |
| 131 } |
| 132 |
| 133 #pragma mark - WidgetViewActionTarget |
| 134 |
| 135 - (void)openSearch:(id)sender { |
| 136 [self openAppWithCommand:base::SysUTF8ToNSString( |
| 137 app_group::kChromeAppGroupFocusOmniboxCommand)]; |
| 138 } |
| 139 |
| 140 - (void)openIncognito:(id)sender { |
| 141 [self |
| 142 openAppWithCommand:base::SysUTF8ToNSString( |
| 143 app_group::kChromeAppGroupIncognitoSearchCommand)]; |
| 144 } |
| 145 |
| 146 - (void)openVoice:(id)sender { |
| 147 [self openAppWithCommand:base::SysUTF8ToNSString( |
| 148 app_group::kChromeAppGroupVoiceSearchCommand)]; |
| 149 } |
| 150 |
| 151 - (void)openQRCode:(id)sender { |
| 152 [self openAppWithCommand:base::SysUTF8ToNSString( |
| 153 app_group::kChromeAppGroupQRScannerCommand)]; |
| 154 } |
| 155 |
| 156 - (void)openCopiedURL:(id)sender { |
| 157 DCHECK(self.copiedURL); |
| 158 [self openAppWithCommand:base::SysUTF8ToNSString( |
| 159 app_group::kChromeAppGroupOpenURLCommand) |
| 160 parameter:self.copiedURL.absoluteString]; |
| 161 } |
| 162 |
| 163 #pragma mark - internal |
| 164 |
| 165 - (void)openAppWithCommand:(NSString*)command { |
| 166 return [self openAppWithCommand:command parameter:nil]; |
| 167 } |
| 168 |
| 169 - (void)openAppWithCommand:(NSString*)command parameter:(NSString*)parameter { |
54 NSUserDefaults* sharedDefaults = | 170 NSUserDefaults* sharedDefaults = |
55 [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]; | 171 [[NSUserDefaults alloc] initWithSuiteName:app_group::ApplicationGroup()]; |
56 NSString* defaultsKey = | 172 NSString* defaultsKey = |
57 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference); | 173 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandPreference); |
58 [sharedDefaults setObject:[WidgetViewController commandDict] | 174 [sharedDefaults setObject:[WidgetViewController dictForCommand:command |
| 175 parameter:parameter] |
59 forKey:defaultsKey]; | 176 forKey:defaultsKey]; |
60 [sharedDefaults synchronize]; | 177 [sharedDefaults synchronize]; |
61 | 178 |
62 NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle] | 179 NSString* scheme = base::mac::ObjCCast<NSString>([[NSBundle mainBundle] |
63 objectForInfoDictionaryKey:@"KSChannelChromeScheme"]); | 180 objectForInfoDictionaryKey:@"KSChannelChromeScheme"]); |
64 if (!scheme) | 181 if (!scheme) |
65 return; | 182 return; |
66 const GURL openURL = | 183 |
67 CreateXCallbackURL(base::SysNSStringToUTF8(scheme), | 184 NSURLComponents* urlComponents = [NSURLComponents new]; |
68 app_group::kChromeAppGroupXCallbackCommand); | 185 urlComponents.scheme = scheme; |
69 [self.extensionContext openURL:net::NSURLWithGURL(openURL) | 186 urlComponents.host = kXCallbackURLHost; |
70 completionHandler:nil]; | 187 urlComponents.path = [NSString |
| 188 stringWithFormat:@"/%@", base::SysUTF8ToNSString( |
| 189 app_group::kChromeAppGroupXCallbackCommand)]; |
| 190 |
| 191 NSURL* openURL = [urlComponents URL]; |
| 192 [self.extensionContext openURL:openURL completionHandler:nil]; |
71 } | 193 } |
72 | 194 |
73 + (NSDictionary*)commandDict { | 195 + (NSDictionary*)dictForCommand:(NSString*)command |
74 NSString* command = | 196 parameter:(NSString*)parameter { |
75 base::SysUTF8ToNSString(app_group::kChromeAppGroupFocusOmniboxCommand); | |
76 NSString* timePrefKey = | 197 NSString* timePrefKey = |
77 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference); | 198 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandTimePreference); |
78 NSString* appPrefKey = | 199 NSString* appPrefKey = |
79 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference); | 200 base::SysUTF8ToNSString(app_group::kChromeAppGroupCommandAppPreference); |
80 NSString* commandPrefKey = base::SysUTF8ToNSString( | 201 NSString* commandPrefKey = base::SysUTF8ToNSString( |
81 app_group::kChromeAppGroupCommandCommandPreference); | 202 app_group::kChromeAppGroupCommandCommandPreference); |
| 203 |
| 204 if (parameter) { |
| 205 NSString* paramPrefKey = base::SysUTF8ToNSString( |
| 206 app_group::kChromeAppGroupCommandParameterPreference); |
| 207 return @{ |
| 208 timePrefKey : [NSDate date], |
| 209 appPrefKey : @"TodayExtension", |
| 210 commandPrefKey : command, |
| 211 paramPrefKey : parameter, |
| 212 }; |
| 213 } |
82 return @{ | 214 return @{ |
83 timePrefKey : [NSDate date], | 215 timePrefKey : [NSDate date], |
84 appPrefKey : @"TodayExtension", | 216 appPrefKey : @"TodayExtension", |
85 commandPrefKey : command, | 217 commandPrefKey : command, |
86 }; | 218 }; |
87 } | 219 } |
88 | 220 |
89 @end | 221 @end |
OLD | NEW |