OLD | NEW |
(Empty) | |
| 1 // Copyright 2012 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 |
| 5 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h" |
| 6 |
| 7 #import <QuartzCore/QuartzCore.h> |
| 8 |
| 9 #import "base/ios/weak_nsobject.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/mac/objc_property_releaser.h" |
| 12 #include "base/metrics/user_metrics.h" |
| 13 #include "base/metrics/user_metrics_action.h" |
| 14 #include "components/prefs/pref_service.h" |
| 15 #include "components/search_engines/template_url_service.h" |
| 16 #include "components/strings/grit/components_strings.h" |
| 17 #include "components/sync_sessions/synced_session.h" |
| 18 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 19 #include "ios/chrome/browser/pref_names.h" |
| 20 #include "ios/chrome/browser/search_engines/template_url_service_factory.h" |
| 21 #include "ios/chrome/browser/sync/sync_setup_service.h" |
| 22 #include "ios/chrome/browser/sync/sync_setup_service_factory.h" |
| 23 #import "ios/chrome/browser/tabs/tab_model.h" |
| 24 #import "ios/chrome/browser/ui/bookmarks/bookmark_controller_factory.h" |
| 25 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" |
| 26 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" |
| 27 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
| 28 #import "ios/chrome/browser/ui/ntp/google_landing_controller.h" |
| 29 #import "ios/chrome/browser/ui/ntp/incognito_panel_controller.h" |
| 30 #import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h" |
| 31 #import "ios/chrome/browser/ui/ntp/new_tab_page_view.h" |
| 32 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h" |
| 33 #import "ios/chrome/browser/ui/rtl_geometry.h" |
| 34 #include "ios/chrome/browser/ui/toolbar/toolbar_model_ios.h" |
| 35 #include "ios/chrome/browser/ui/ui_util.h" |
| 36 #include "ios/chrome/grit/ios_strings.h" |
| 37 #import "ios/web/web_state/ui/crw_swipe_recognizer_provider.h" |
| 38 #include "ui/base/l10n/l10n_util.h" |
| 39 #include "ui/base/l10n/l10n_util_mac.h" |
| 40 |
| 41 using base::UserMetricsAction; |
| 42 |
| 43 namespace { |
| 44 const char* kMostVisitedFragment = "most_visited"; |
| 45 const char* kBookmarksFragment = "bookmarks"; |
| 46 const char* kOpenTabsFragment = "open_tabs"; |
| 47 const char* kIncognitoFragment = "incognito"; |
| 48 const CGFloat kToolbarHeight = 56; |
| 49 } |
| 50 |
| 51 namespace NewTabPage { |
| 52 |
| 53 // Converts from a URL #fragment string to an identifier. |
| 54 // Defaults to NewTabPage::kNone if the fragment is nil or not recognized. |
| 55 // The strings checked by this function matches the set of fragments |
| 56 // supported by chrome://newtab/# on Android. |
| 57 // See chrome/browser/resources/mobile_ntp/mobile_ntp.js |
| 58 PanelIdentifier IdentifierFromFragment(const std::string& fragment) { |
| 59 if (fragment == kMostVisitedFragment) |
| 60 return NewTabPage::kMostVisitedPanel; |
| 61 else if (fragment == kBookmarksFragment) |
| 62 return NewTabPage::kBookmarksPanel; |
| 63 else if (fragment == kOpenTabsFragment) |
| 64 return NewTabPage::kOpenTabsPanel; |
| 65 else if (fragment == kIncognitoFragment) |
| 66 return NewTabPage::kIncognitoPanel; |
| 67 else |
| 68 return NewTabPage::kNone; |
| 69 } |
| 70 |
| 71 // Converts from a NewTabPage::PanelIdentifier to a URL #fragment string. |
| 72 // Defaults to nil if the fragment is kNone or not recognized. |
| 73 std::string FragmentFromIdentifier(PanelIdentifier panel) { |
| 74 switch (panel) { |
| 75 case NewTabPage::kNone: |
| 76 return ""; |
| 77 case NewTabPage::kMostVisitedPanel: |
| 78 return kMostVisitedFragment; |
| 79 case NewTabPage::kBookmarksPanel: |
| 80 return kBookmarksFragment; |
| 81 case NewTabPage::kOpenTabsPanel: |
| 82 return kOpenTabsFragment; |
| 83 case NewTabPage::kIncognitoPanel: |
| 84 return kIncognitoFragment; |
| 85 default: |
| 86 NOTREACHED(); |
| 87 return ""; |
| 88 } |
| 89 } |
| 90 |
| 91 } // namespace NewTabPage |
| 92 |
| 93 namespace { |
| 94 |
| 95 // TODO(pkl): These are private constants and enums from |
| 96 // ui/webui/ntp/new_tab_page_handler.h. At some point these should be |
| 97 // refactored out so they can be included instead of redefined here. |
| 98 const int kPageIdOffset = 10; |
| 99 enum { |
| 100 INDEX_MASK = (1 << kPageIdOffset) - 1, |
| 101 MOST_VISITED_PAGE_ID = 1 << kPageIdOffset, |
| 102 BOOKMARKS_PAGE_ID = 3 << kPageIdOffset, |
| 103 OPEN_TABS_PAGE_ID = 4 << kPageIdOffset, |
| 104 }; |
| 105 |
| 106 } // anonymous namespace |
| 107 |
| 108 @interface NewTabPageController () { |
| 109 ios::ChromeBrowserState* browserState_; // Weak. |
| 110 id<UrlLoader> loader_; // Weak. |
| 111 id<CRWSwipeRecognizerProvider> swipeRecognizerProvider_; // Weak. |
| 112 id<NewTabPageControllerObserver> newTabPageObserver_; // Weak. |
| 113 |
| 114 NewTabPageView* newTabPageView_; |
| 115 |
| 116 base::scoped_nsobject<RecentTabsPanelController> openTabsController_; |
| 117 // Has the scrollView been initialized. |
| 118 BOOL scrollInitialized_; |
| 119 |
| 120 // Dominant color cache. Key: (NSString*)url, val: (UIColor*)dominantColor. |
| 121 NSMutableDictionary* dominantColorCache_; // Weak, owned by bvc. |
| 122 |
| 123 // Delegate to focus and blur the omnibox. |
| 124 base::WeakNSProtocol<id<OmniboxFocuser>> focuser_; |
| 125 |
| 126 // Delegate to fetch the ToolbarModel and current web state from. |
| 127 base::WeakNSProtocol<id<WebToolbarDelegate>> webToolbarDelegate_; |
| 128 |
| 129 base::scoped_nsobject<TabModel> tabModel_; |
| 130 |
| 131 base::mac::ObjCPropertyReleaser propertyReleaser_NewTabPageController_; |
| 132 } |
| 133 |
| 134 // Load and bring panel into view. |
| 135 - (void)showPanel:(NewTabPageBarItem*)item; |
| 136 // Load panel on demand. |
| 137 - (BOOL)loadPanel:(NewTabPageBarItem*)item; |
| 138 // After a panel changes, update metrics and prefs information. |
| 139 - (void)panelChanged:(NewTabPageBarItem*)item; |
| 140 // Update current controller and tab bar index. Used to call reload. |
| 141 - (void)updateCurrentController:(NewTabPageBarItem*)item |
| 142 index:(NSUInteger)index; |
| 143 // Bring panel into scroll view. |
| 144 - (void)scrollToPanel:(NewTabPageBarItem*)item animate:(BOOL)animate; |
| 145 // Returns index of item in tab bar. |
| 146 - (NSUInteger)tabBarItemIndex:(NewTabPageBarItem*)item; |
| 147 // Call loadPanel by item index. |
| 148 - (void)loadControllerWithIndex:(NSUInteger)index; |
| 149 // Initialize scroll view. |
| 150 - (void)setUpScrollView; |
| 151 // Update overlay scroll view value. |
| 152 - (void)updateOverlayScrollPosition; |
| 153 // Disable the horizontal scroll view. |
| 154 - (void)disableScroll; |
| 155 // Enable the horizontal scroll view. |
| 156 - (void)enableScroll; |
| 157 // Returns the ID for the currently selected panel. |
| 158 - (NewTabPage::PanelIdentifier)selectedPanelID; |
| 159 |
| 160 @property(nonatomic, retain) NewTabPageView* ntpView; |
| 161 @end |
| 162 |
| 163 @implementation NewTabPageController |
| 164 |
| 165 @synthesize ntpView = newTabPageView_; |
| 166 @synthesize swipeRecognizerProvider = swipeRecognizerProvider_; |
| 167 |
| 168 - (id)initWithUrl:(const GURL&)url |
| 169 loader:(id<UrlLoader>)loader |
| 170 focuser:(id<OmniboxFocuser>)focuser |
| 171 ntpObserver:(id<NewTabPageControllerObserver>)ntpObserver |
| 172 browserState:(ios::ChromeBrowserState*)browserState |
| 173 colorCache:(NSMutableDictionary*)colorCache |
| 174 webToolbarDelegate:(id<WebToolbarDelegate>)webToolbarDelegate |
| 175 tabModel:(TabModel*)tabModel { |
| 176 self = [super initWithNibName:nil url:url]; |
| 177 if (self) { |
| 178 DCHECK(browserState); |
| 179 propertyReleaser_NewTabPageController_.Init(self, |
| 180 [NewTabPageController class]); |
| 181 browserState_ = browserState; |
| 182 loader_ = loader; |
| 183 newTabPageObserver_ = ntpObserver; |
| 184 focuser_.reset(focuser); |
| 185 webToolbarDelegate_.reset(webToolbarDelegate); |
| 186 tabModel_.reset([tabModel retain]); |
| 187 dominantColorCache_ = colorCache; |
| 188 self.title = l10n_util::GetNSString(IDS_NEW_TAB_TITLE); |
| 189 scrollInitialized_ = NO; |
| 190 |
| 191 base::scoped_nsobject<UIScrollView> scrollView( |
| 192 [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 412)]); |
| 193 [scrollView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | |
| 194 UIViewAutoresizingFlexibleHeight)]; |
| 195 base::scoped_nsobject<NewTabPageBar> tabBar( |
| 196 [[NewTabPageBar alloc] initWithFrame:CGRectMake(0, 412, 320, 48)]); |
| 197 newTabPageView_ = |
| 198 [[NewTabPageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460) |
| 199 andScrollView:scrollView |
| 200 andTabBar:tabBar]; |
| 201 // TODO(crbug.com/607113): Merge view and ntpView. |
| 202 self.view = newTabPageView_; |
| 203 [tabBar setDelegate:self]; |
| 204 |
| 205 bool isIncognito = browserState_->IsOffTheRecord(); |
| 206 |
| 207 NSString* incognito = l10n_util::GetNSString(IDS_IOS_NEW_TAB_INCOGNITO); |
| 208 NSString* mostVisited = |
| 209 l10n_util::GetNSString(IDS_IOS_NEW_TAB_MOST_VISITED); |
| 210 NSString* bookmarks = |
| 211 l10n_util::GetNSString(IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE); |
| 212 NSString* openTabs = l10n_util::GetNSString(IDS_IOS_NEW_TAB_RECENT_TABS); |
| 213 |
| 214 NSMutableArray* tabBarItems = [NSMutableArray array]; |
| 215 NewTabPageBarItem* itemToDisplay = nil; |
| 216 if (isIncognito) { |
| 217 NewTabPageBarItem* incognitoItem = [NewTabPageBarItem |
| 218 newTabPageBarItemWithTitle:incognito |
| 219 identifier:NewTabPage::kIncognitoPanel |
| 220 image:[UIImage imageNamed:@"ntp_incognito"]]; |
| 221 if (IsIPadIdiom()) { |
| 222 // Only add the bookmarks tab item for Incognito. |
| 223 NewTabPageBarItem* bookmarksItem = [NewTabPageBarItem |
| 224 newTabPageBarItemWithTitle:bookmarks |
| 225 identifier:NewTabPage::kBookmarksPanel |
| 226 image:[UIImage imageNamed:@"ntp_bookmarks"]]; |
| 227 [tabBarItems addObject:bookmarksItem]; |
| 228 [tabBarItems addObject:incognitoItem]; |
| 229 self.ntpView.tabBar.items = tabBarItems; |
| 230 } |
| 231 itemToDisplay = incognitoItem; |
| 232 } else { |
| 233 NewTabPageBarItem* mostVisitedItem = [NewTabPageBarItem |
| 234 newTabPageBarItemWithTitle:mostVisited |
| 235 identifier:NewTabPage::kMostVisitedPanel |
| 236 image:[UIImage imageNamed:@"ntp_mv_search"]]; |
| 237 NewTabPageBarItem* bookmarksItem = [NewTabPageBarItem |
| 238 newTabPageBarItemWithTitle:bookmarks |
| 239 identifier:NewTabPage::kBookmarksPanel |
| 240 image:[UIImage imageNamed:@"ntp_bookmarks"]]; |
| 241 [tabBarItems addObject:bookmarksItem]; |
| 242 if (IsIPadIdiom()) { |
| 243 [tabBarItems addObject:mostVisitedItem]; |
| 244 } |
| 245 |
| 246 NewTabPageBarItem* openTabsItem = [NewTabPageBarItem |
| 247 newTabPageBarItemWithTitle:openTabs |
| 248 identifier:NewTabPage::kOpenTabsPanel |
| 249 image:[UIImage imageNamed:@"ntp_opentabs"]]; |
| 250 [tabBarItems addObject:openTabsItem]; |
| 251 self.ntpView.tabBar.items = tabBarItems; |
| 252 |
| 253 if (!IsIPadIdiom()) { |
| 254 itemToDisplay = mostVisitedItem; |
| 255 } else { |
| 256 PrefService* prefs = browserState_->GetPrefs(); |
| 257 int shownPage = prefs->GetInteger(prefs::kNtpShownPage); |
| 258 shownPage = shownPage & ~INDEX_MASK; |
| 259 |
| 260 if (shownPage == BOOKMARKS_PAGE_ID) { |
| 261 itemToDisplay = bookmarksItem; |
| 262 } else if (shownPage == OPEN_TABS_PAGE_ID) { |
| 263 itemToDisplay = openTabsItem; |
| 264 } else { |
| 265 itemToDisplay = mostVisitedItem; |
| 266 } |
| 267 } |
| 268 } |
| 269 DCHECK(itemToDisplay); |
| 270 [self setUpScrollView]; |
| 271 [self showPanel:itemToDisplay]; |
| 272 [self updateOverlayScrollPosition]; |
| 273 } |
| 274 return self; |
| 275 } |
| 276 |
| 277 - (void)dealloc { |
| 278 // Animations can last past the life of the NTP controller, nil out the |
| 279 // delegate. |
| 280 self.ntpView.scrollView.delegate = nil; |
| 281 [googleLandingController_ setDelegate:nil]; |
| 282 [bookmarkController_ setDelegate:nil]; |
| 283 [openTabsController_ setDelegate:nil]; |
| 284 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 285 [super dealloc]; |
| 286 } |
| 287 |
| 288 #pragma mark - CRWNativeContent |
| 289 |
| 290 // Note: No point implementing -handleLowMemory because all native content |
| 291 // views but the selected one are dropped, and the selected view doesn't |
| 292 // need to do anything. |
| 293 |
| 294 - (void)reload { |
| 295 [currentController_ reload]; |
| 296 [super reload]; |
| 297 } |
| 298 |
| 299 - (void)wasShown { |
| 300 [currentController_ wasShown]; |
| 301 // Ensure that the NTP has the latest data when it is shown. |
| 302 [self reload]; |
| 303 [self.ntpView.tabBar updateColorsForScrollView:self.ntpView.scrollView]; |
| 304 [self.ntpView.tabBar |
| 305 setShadowAlpha:[currentController_ alphaForBottomShadow]]; |
| 306 } |
| 307 |
| 308 - (void)wasHidden { |
| 309 [currentController_ wasHidden]; |
| 310 } |
| 311 |
| 312 - (BOOL)wantsKeyboardShield { |
| 313 return [self selectedPanelID] != NewTabPage::kMostVisitedPanel; |
| 314 } |
| 315 |
| 316 - (BOOL)wantsLocationBarHintText { |
| 317 // Always show hint text on iPhone. |
| 318 if (!IsIPadIdiom()) |
| 319 return YES; |
| 320 // Always show the location bar hint text if the search engine is not Google. |
| 321 TemplateURLService* service = |
| 322 ios::TemplateURLServiceFactory::GetForBrowserState(browserState_); |
| 323 if (service) { |
| 324 TemplateURL* defaultURL = service->GetDefaultSearchProvider(); |
| 325 if (defaultURL && |
| 326 defaultURL->GetEngineType(service->search_terms_data()) != |
| 327 SEARCH_ENGINE_GOOGLE) { |
| 328 return YES; |
| 329 } |
| 330 } |
| 331 |
| 332 // Always return true when incognito. |
| 333 if (browserState_->IsOffTheRecord()) |
| 334 return YES; |
| 335 |
| 336 return [self selectedPanelID] != NewTabPage::kMostVisitedPanel; |
| 337 } |
| 338 |
| 339 - (void)dismissKeyboard { |
| 340 [currentController_ dismissKeyboard]; |
| 341 } |
| 342 |
| 343 - (void)dismissModals { |
| 344 [currentController_ dismissModals]; |
| 345 } |
| 346 |
| 347 - (void)willUpdateSnapshot { |
| 348 if ([currentController_ respondsToSelector:@selector(willUpdateSnapshot)]) { |
| 349 [currentController_ willUpdateSnapshot]; |
| 350 } |
| 351 } |
| 352 |
| 353 #pragma mark - |
| 354 |
| 355 - (void)setSwipeRecognizerProvider:(id<CRWSwipeRecognizerProvider>)provider { |
| 356 swipeRecognizerProvider_ = provider; |
| 357 NSSet* recognizers = [swipeRecognizerProvider_ swipeRecognizers]; |
| 358 for (UISwipeGestureRecognizer* swipeRecognizer in recognizers) { |
| 359 [self.ntpView.scrollView.panGestureRecognizer |
| 360 requireGestureRecognizerToFail:swipeRecognizer]; |
| 361 } |
| 362 } |
| 363 |
| 364 - (void)setUpScrollView { |
| 365 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; |
| 366 [defaultCenter addObserver:self |
| 367 selector:@selector(disableScroll) |
| 368 name:UIKeyboardWillShowNotification |
| 369 object:nil]; |
| 370 [defaultCenter addObserver:self |
| 371 selector:@selector(enableScroll) |
| 372 name:UIKeyboardWillHideNotification |
| 373 object:nil]; |
| 374 |
| 375 UIScrollView* scrollView = self.ntpView.scrollView; |
| 376 scrollView.pagingEnabled = YES; |
| 377 scrollView.showsHorizontalScrollIndicator = NO; |
| 378 scrollView.showsVerticalScrollIndicator = NO; |
| 379 scrollView.contentMode = UIViewContentModeScaleAspectFit; |
| 380 scrollView.bounces = YES; |
| 381 scrollView.delegate = self; |
| 382 scrollView.scrollsToTop = NO; |
| 383 |
| 384 [self.ntpView updateScrollViewContentSize]; |
| 385 [self.ntpView.tabBar updateColorsForScrollView:scrollView]; |
| 386 |
| 387 scrollInitialized_ = YES; |
| 388 } |
| 389 |
| 390 - (void)disableScroll { |
| 391 [self.ntpView.scrollView setScrollEnabled:NO]; |
| 392 } |
| 393 |
| 394 - (void)enableScroll { |
| 395 [self.ntpView.scrollView setScrollEnabled:YES]; |
| 396 } |
| 397 |
| 398 // Update selectedIndex and scroll position as the scroll view moves. |
| 399 - (void)scrollViewDidScroll:(UIScrollView*)scrollView { |
| 400 if (!scrollInitialized_) |
| 401 return; |
| 402 |
| 403 // Position is used to track the exact X position of the scroll view, whereas |
| 404 // index is rounded to the panel that is most visible. |
| 405 CGFloat panelWidth = |
| 406 scrollView.contentSize.width / self.ntpView.tabBar.items.count; |
| 407 LayoutOffset position = |
| 408 LeadingContentOffsetForScrollView(scrollView) / panelWidth; |
| 409 NSUInteger index = round(position); |
| 410 |
| 411 // |scrollView| can be out of range when the frame changes. |
| 412 if (index >= self.ntpView.tabBar.items.count) |
| 413 return; |
| 414 |
| 415 // Only create views when they need to be visible. This will create a slight |
| 416 // jank on first creation, but it doesn't seem very noticable. The trade off |
| 417 // is loading the adjacent panels, and a longer initial NTP startup. |
| 418 if (position - index > 0) |
| 419 [self loadControllerWithIndex:index + 1]; |
| 420 [self loadControllerWithIndex:index]; |
| 421 if (position - index < 0) |
| 422 [self loadControllerWithIndex:index - 1]; |
| 423 |
| 424 // If index changed, follow same path as if a tab bar item was pressed. When |
| 425 // |index| == |position|, the panel is completely in view. |
| 426 if (index == position && self.ntpView.tabBar.selectedIndex != index) { |
| 427 NewTabPageBarItem* item = [self.ntpView.tabBar.items objectAtIndex:index]; |
| 428 DCHECK(item); |
| 429 self.ntpView.tabBar.selectedIndex = index; |
| 430 [self updateCurrentController:item index:index]; |
| 431 [self newTabBarItemDidChange:item changePanel:NO]; |
| 432 } |
| 433 |
| 434 [self.ntpView.tabBar updateColorsForScrollView:scrollView]; |
| 435 [self updateOverlayScrollPosition]; |
| 436 } |
| 437 |
| 438 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView { |
| 439 NSUInteger index = self.ntpView.tabBar.selectedIndex; |
| 440 NewTabPageBarItem* item = [self.ntpView.tabBar.items objectAtIndex:index]; |
| 441 DCHECK(item); |
| 442 [self updateCurrentController:item index:index]; |
| 443 } |
| 444 |
| 445 // Called when the user presses a segment that's not currently selected. |
| 446 // Pressing a segment that's already selected does not trigger this action. |
| 447 - (void)newTabBarItemDidChange:(NewTabPageBarItem*)selectedItem |
| 448 changePanel:(BOOL)changePanel { |
| 449 [self panelChanged:selectedItem]; |
| 450 if (changePanel) { |
| 451 [self scrollToPanel:selectedItem animate:YES]; |
| 452 } |
| 453 |
| 454 [newTabPageObserver_ selectedPanelDidChange]; |
| 455 } |
| 456 |
| 457 - (void)selectPanel:(NewTabPage::PanelIdentifier)panelType { |
| 458 for (NewTabPageBarItem* item in self.ntpView.tabBar.items) { |
| 459 if (item.identifier == panelType) { |
| 460 [self showPanel:item]; |
| 461 return; // Early return after finding the first match. |
| 462 } |
| 463 } |
| 464 } |
| 465 |
| 466 - (void)showPanel:(NewTabPageBarItem*)item { |
| 467 if ([self loadPanel:item]) { |
| 468 // Intentionally omitting a metric for the Incognito panel. |
| 469 if (item.identifier == NewTabPage::kBookmarksPanel) |
| 470 base::RecordAction(UserMetricsAction("MobileNTPShowBookmarks")); |
| 471 else if (item.identifier == NewTabPage::kMostVisitedPanel) |
| 472 base::RecordAction(UserMetricsAction("MobileNTPShowMostVisited")); |
| 473 else if (item.identifier == NewTabPage::kOpenTabsPanel) |
| 474 base::RecordAction(UserMetricsAction("MobileNTPShowOpenTabs")); |
| 475 } |
| 476 [self scrollToPanel:item animate:NO]; |
| 477 } |
| 478 |
| 479 - (void)loadControllerWithIndex:(NSUInteger)index { |
| 480 if (index >= self.ntpView.tabBar.items.count) |
| 481 return; |
| 482 |
| 483 NewTabPageBarItem* item = [self.ntpView.tabBar.items objectAtIndex:index]; |
| 484 [self loadPanel:item]; |
| 485 } |
| 486 |
| 487 - (BOOL)loadPanel:(NewTabPageBarItem*)item { |
| 488 UIView* view; |
| 489 BOOL created = NO; |
| 490 // Only load the controllers once. |
| 491 if (item.identifier == NewTabPage::kBookmarksPanel) { |
| 492 if (!bookmarkController_) { |
| 493 base::scoped_nsobject<BookmarkControllerFactory> factory( |
| 494 [[BookmarkControllerFactory alloc] init]); |
| 495 bookmarkController_.reset([[factory |
| 496 bookmarkPanelControllerForBrowserState:browserState_ |
| 497 loader:loader_ |
| 498 colorCache:dominantColorCache_] retain]); |
| 499 } |
| 500 view = [bookmarkController_ view]; |
| 501 [bookmarkController_ setDelegate:self]; |
| 502 } else if (item.identifier == NewTabPage::kMostVisitedPanel) { |
| 503 if (!googleLandingController_) { |
| 504 googleLandingController_.reset([[GoogleLandingController alloc] |
| 505 initWithLoader:loader_ |
| 506 browserState:browserState_ |
| 507 focuser:focuser_ |
| 508 webToolbarDelegate:webToolbarDelegate_ |
| 509 tabModel:tabModel_]); |
| 510 } |
| 511 view = [googleLandingController_ view]; |
| 512 [googleLandingController_ setDelegate:self]; |
| 513 } else if (item.identifier == NewTabPage::kOpenTabsPanel) { |
| 514 if (!openTabsController_) |
| 515 openTabsController_.reset([[RecentTabsPanelController alloc] |
| 516 initWithLoader:loader_ |
| 517 browserState:browserState_]); |
| 518 view = [openTabsController_ view]; |
| 519 [openTabsController_ setDelegate:self]; |
| 520 } else if (item.identifier == NewTabPage::kIncognitoPanel) { |
| 521 if (!incognitoController_) |
| 522 incognitoController_.reset([[IncognitoPanelController alloc] |
| 523 initWithLoader:loader_ |
| 524 browserState:browserState_ |
| 525 webToolbarDelegate:webToolbarDelegate_]); |
| 526 view = [incognitoController_ view]; |
| 527 } else { |
| 528 NOTREACHED(); |
| 529 return NO; |
| 530 } |
| 531 |
| 532 // Add the panel views to the scroll view in the proper location. |
| 533 NSUInteger index = [self tabBarItemIndex:item]; |
| 534 if (view.superview == nil) { |
| 535 created = YES; |
| 536 view.frame = [self.ntpView panelFrameForItemAtIndex:index]; |
| 537 item.view = view; |
| 538 [self.ntpView.scrollView addSubview:view]; |
| 539 } |
| 540 return created; |
| 541 } |
| 542 |
| 543 - (void)scrollToPanel:(NewTabPageBarItem*)item animate:(BOOL)animate { |
| 544 NSUInteger index = [self tabBarItemIndex:item]; |
| 545 if (IsIPadIdiom()) { |
| 546 CGRect itemFrame = [self.ntpView panelFrameForItemAtIndex:index]; |
| 547 CGPoint point = CGPointMake(CGRectGetMinX(itemFrame), 0); |
| 548 [self.ntpView.scrollView setContentOffset:point animated:animate]; |
| 549 } else { |
| 550 if (item.identifier == NewTabPage::kBookmarksPanel) { |
| 551 base::scoped_nsobject<GenericChromeCommand> command( |
| 552 [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_BOOKMARK_MANAGER]); |
| 553 [self.ntpView chromeExecuteCommand:command]; |
| 554 } else if (item.identifier == NewTabPage::kOpenTabsPanel) { |
| 555 base::scoped_nsobject<GenericChromeCommand> command( |
| 556 [[GenericChromeCommand alloc] initWithTag:IDC_SHOW_OTHER_DEVICES]); |
| 557 [self.ntpView chromeExecuteCommand:command]; |
| 558 } |
| 559 } |
| 560 |
| 561 if (currentController_ == nil) { |
| 562 [self updateCurrentController:item index:index]; |
| 563 } |
| 564 } |
| 565 |
| 566 // Return the index of the tab item. For iPhone always return 0 since the |
| 567 // returned index is used to update the visible controller and scroll the NTP |
| 568 // scroll view. None of this is applicable for iPhone. |
| 569 - (NSUInteger)tabBarItemIndex:(NewTabPageBarItem*)item { |
| 570 NSUInteger index = 0; |
| 571 if (IsIPadIdiom()) { |
| 572 index = [self.ntpView.tabBar.items indexOfObject:item]; |
| 573 DCHECK(index != NSNotFound); |
| 574 } |
| 575 return index; |
| 576 } |
| 577 |
| 578 - (NewTabPage::PanelIdentifier)selectedPanelID { |
| 579 if (IsIPadIdiom()) { |
| 580 // |selectedIndex| isn't meaningful here with modal buttons on iPhone. |
| 581 NSUInteger index = self.ntpView.tabBar.selectedIndex; |
| 582 DCHECK(index != NSNotFound); |
| 583 NewTabPageBarItem* item = self.ntpView.tabBar.items[index]; |
| 584 return static_cast<NewTabPage::PanelIdentifier>(item.identifier); |
| 585 } |
| 586 return NewTabPage::kMostVisitedPanel; |
| 587 } |
| 588 |
| 589 - (void)updateCurrentController:(NewTabPageBarItem*)item |
| 590 index:(NSUInteger)index { |
| 591 if (!IsIPadIdiom() && (item.identifier == NewTabPage::kBookmarksPanel || |
| 592 item.identifier == NewTabPage::kOpenTabsPanel)) { |
| 593 // Don't update |currentController_| for iPhone since Bookmarks and Recent |
| 594 // Tabs are presented in a modal view controller. |
| 595 return; |
| 596 } |
| 597 |
| 598 id<NewTabPagePanelProtocol> oldController = currentController_; |
| 599 self.ntpView.tabBar.selectedIndex = index; |
| 600 if (item.identifier == NewTabPage::kBookmarksPanel) |
| 601 currentController_ = bookmarkController_.get(); |
| 602 else if (item.identifier == NewTabPage::kMostVisitedPanel) |
| 603 currentController_ = googleLandingController_.get(); |
| 604 else if (item.identifier == NewTabPage::kOpenTabsPanel) |
| 605 currentController_ = openTabsController_.get(); |
| 606 else if (item.identifier == NewTabPage::kIncognitoPanel) |
| 607 currentController_ = incognitoController_.get(); |
| 608 |
| 609 [bookmarkController_ |
| 610 setScrollsToTop:(currentController_ == bookmarkController_.get())]; |
| 611 [googleLandingController_ |
| 612 setScrollsToTop:(currentController_ == googleLandingController_.get())]; |
| 613 [openTabsController_ |
| 614 setScrollsToTop:(currentController_ == openTabsController_.get())]; |
| 615 [self.ntpView.tabBar |
| 616 setShadowAlpha:[currentController_ alphaForBottomShadow]]; |
| 617 |
| 618 if (oldController != currentController_) { |
| 619 [currentController_ wasShown]; |
| 620 [oldController wasHidden]; |
| 621 } |
| 622 } |
| 623 |
| 624 - (void)panelChanged:(NewTabPageBarItem*)item { |
| 625 if (browserState_->IsOffTheRecord()) |
| 626 return; |
| 627 |
| 628 // Save state and update metrics. Intentionally omitting a metric for the |
| 629 // Incognito panel. |
| 630 PrefService* prefs = browserState_->GetPrefs(); |
| 631 if (item.identifier == NewTabPage::kBookmarksPanel) { |
| 632 base::RecordAction(UserMetricsAction("MobileNTPSwitchToBookmarks")); |
| 633 prefs->SetInteger(prefs::kNtpShownPage, BOOKMARKS_PAGE_ID); |
| 634 } else if (item.identifier == NewTabPage::kMostVisitedPanel) { |
| 635 base::RecordAction(UserMetricsAction("MobileNTPSwitchToMostVisited")); |
| 636 prefs->SetInteger(prefs::kNtpShownPage, MOST_VISITED_PAGE_ID); |
| 637 } else if (item.identifier == NewTabPage::kOpenTabsPanel) { |
| 638 base::RecordAction(UserMetricsAction("MobileNTPSwitchToOpenTabs")); |
| 639 prefs->SetInteger(prefs::kNtpShownPage, OPEN_TABS_PAGE_ID); |
| 640 } |
| 641 } |
| 642 |
| 643 - (void)updateOverlayScrollPosition { |
| 644 // Update overlay position. This moves the overlay animation on the tab bar. |
| 645 UIScrollView* scrollView = self.ntpView.scrollView; |
| 646 if (!scrollView || scrollView.contentSize.width == 0.0) |
| 647 return; |
| 648 self.ntpView.tabBar.overlayPercentage = |
| 649 scrollView.contentOffset.x / scrollView.contentSize.width; |
| 650 } |
| 651 |
| 652 #pragma mark - LogoAnimationControllerOwnerOwner |
| 653 |
| 654 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner { |
| 655 return [googleLandingController_ logoAnimationControllerOwner]; |
| 656 } |
| 657 |
| 658 #pragma mark - |
| 659 #pragma mark ToolbarOwner |
| 660 |
| 661 - (ToolbarController*)relinquishedToolbarController { |
| 662 return [googleLandingController_ relinquishedToolbarController]; |
| 663 } |
| 664 |
| 665 - (void)reparentToolbarController { |
| 666 [googleLandingController_ reparentToolbarController]; |
| 667 } |
| 668 |
| 669 - (CGFloat)toolbarHeight { |
| 670 // If the google landing controller is nil, there is no toolbar visible in the |
| 671 // native content view, finally there is no toolbar on iPad. |
| 672 return googleLandingController_ && !IsIPadIdiom() ? kToolbarHeight : 0.0; |
| 673 } |
| 674 |
| 675 #pragma mark - NewTabPagePanelControllerDelegate |
| 676 |
| 677 - (void)updateNtpBarShadowForPanelController: |
| 678 (id<NewTabPagePanelProtocol>)ntpPanelController { |
| 679 if (currentController_ != ntpPanelController) |
| 680 return; |
| 681 [self.ntpView.tabBar |
| 682 setShadowAlpha:[ntpPanelController alphaForBottomShadow]]; |
| 683 } |
| 684 |
| 685 @end |
OLD | NEW |