chromium/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/tab_grid_transition_handler.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/tab_grid_transition_handler.h"

#import "base/check.h"
#import "base/ios/block_types.h"
#import "ios/chrome/browser/shared/ui/util/named_guide.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/centered_zoom_transition_animation.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/point_zoom_transition_animation.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/tab_grid_transition_animation.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/tab_grid_transition_animation_group.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/tab_grid_transition_item.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/tab_grid_transition_layout.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/tab_grid_transition_layout_providing.h"

@implementation TabGridTransitionHandler {
  TabGridTransitionType _transitionType;
  TabGridTransitionDirection _direction;

  UIViewController<TabGridTransitionLayoutProviding>* _tabGridViewController;
  UIViewController* _BVCContainerViewController;

  // Transition item for the selected cell in tab grid.
  TabGridTransitionItem* _tabGridCellItem;

  // Transition animation to execute.
  id<TabGridTransitionAnimation> _animation;
}

#pragma mark - Public

- (instancetype)initWithTransitionType:(TabGridTransitionType)transitionType
                             direction:(TabGridTransitionDirection)direction
                 tabGridViewController:
                     (UIViewController<TabGridTransitionLayoutProviding>*)
                         tabGridViewController
            bvcContainerViewController:
                (UIViewController*)bvcContainerViewController {
  self = [super init];
  if (self) {
    CHECK(tabGridViewController.transitionLayout);

    _transitionType = transitionType;
    _direction = direction;
    _tabGridViewController = tabGridViewController;
    _BVCContainerViewController = bvcContainerViewController;
    _tabGridCellItem = tabGridViewController.transitionLayout.activeCell;
  }
  return self;
}

- (void)performTransitionWithCompletion:(ProceduralBlock)completion {
  switch (_direction) {
    case TabGridTransitionDirection::kFromBrowserToTabGrid:
      [self performBrowserToTabGridTransitionWithCompletion:completion];
      break;

    case TabGridTransitionDirection::kFromTabGridToBrowser:
      [self performTabGridToBrowserTransitionWithCompletion:completion];
      break;
  }
}

#pragma mark - Private

// Performs the Browser to Tab Grid transition with a `completion` block.
- (void)performBrowserToTabGridTransitionWithCompletion:
    (ProceduralBlock)completion {
  __weak __typeof(self) weakSelf = self;
  ProceduralBlock animationCompletion = ^{
    [weakSelf finalizeBrowserToTabGridTransition];

    if (completion) {
      completion();
    }
  };

  [self prepareBrowserToTabGridTransition];
  [self performBrowserToTabGridTransitionAnimationWithCompletion:
            animationCompletion];
}

// Performs the Tab Grid to Browser transition with a `completion` block.
- (void)performTabGridToBrowserTransitionWithCompletion:
    (ProceduralBlock)completion {
  __weak __typeof(self) weakSelf = self;
  ProceduralBlock animationCompletion = ^{
    [weakSelf finalizeTabGridToBrowserTransition];

    if (completion) {
      completion();
    }
  };

  [self prepareTabGridToBrowserTransition];
  [self performTabGridToBrowserTransitionAnimationWithCompletion:
            animationCompletion];
}

// Prepares items for the Browser to Tab Grid transition.
- (void)prepareBrowserToTabGridTransition {
  [_BVCContainerViewController willMoveToParentViewController:nil];
}

// Prepares items for the Tab Grid to Browser transition.
- (void)prepareTabGridToBrowserTransition {
  [_tabGridViewController addChildViewController:_BVCContainerViewController];
  _BVCContainerViewController.view.frame = _tabGridViewController.view.bounds;
  [_tabGridViewController.view addSubview:_BVCContainerViewController.view];

  _BVCContainerViewController.view.accessibilityViewIsModal = YES;
}

// Performs Browser to TabGrid transition animation.
- (void)performBrowserToTabGridTransitionAnimationWithCompletion:
    (ProceduralBlock)completion {
  _animation = [self determineAnimationForBrowserToTabGridTransition];
  [self performTransitionAnimationWithCompletion:completion];
}

// Performs TabGrid to Browser transition animation.
- (void)performTabGridToBrowserTransitionAnimationWithCompletion:
    (ProceduralBlock)completion {
  _animation = [self determineAnimationForTabGridToBrowserTransition];
  [self performTransitionAnimationWithCompletion:completion];
}

// Takes all necessary actions to finish Browser to TabGrid transition.
- (void)finalizeBrowserToTabGridTransition {
  [_BVCContainerViewController.view removeFromSuperview];
  [_BVCContainerViewController removeFromParentViewController];

  [_tabGridViewController setNeedsStatusBarAppearanceUpdate];
}

// Takes all necessary actions to finish TabGrid to Browser transition.
- (void)finalizeTabGridToBrowserTransition {
  [_BVCContainerViewController
      didMoveToParentViewController:_tabGridViewController];

  [_BVCContainerViewController setNeedsStatusBarAppearanceUpdate];
}

// Performs transition animation.
- (void)performTransitionAnimationWithCompletion:(ProceduralBlock)completion {
  if (_animation) {
    [_animation animateWithCompletion:completion];

  } else if (completion) {
    completion();
  }
}

// Determines the proper animation that should be used in Browser to TabGrid
// transition.
- (id<TabGridTransitionAnimation>)
    determineAnimationForBrowserToTabGridTransition {
  switch (_transitionType) {
    case TabGridTransitionType::kNormal:
      return [self browserToTabGridNormalAnimation];
    case TabGridTransitionType::kReducedMotion:
      return [self browserToTabGridReducedMotionAnimation];
    case TabGridTransitionType::kAnimationDisabled:
      return nil;
  }
}

// Determines the proper animation that should be used in TabGrid to Browser
// transition.
- (id<TabGridTransitionAnimation>)
    determineAnimationForTabGridToBrowserTransition {
  switch (_transitionType) {
    case TabGridTransitionType::kNormal:
      return [self tabGridToBrowserNormalAnimation];
    case TabGridTransitionType::kReducedMotion:
      return [self tabGridToBrowserReducedMotionAnimation];
    case TabGridTransitionType::kAnimationDisabled:
      return nil;
  }
}

// Returns Browser to TabGrid normal motion animation.
// TODO(crbug.com/40287759): Finish this animation.
- (id<TabGridTransitionAnimation>)browserToTabGridNormalAnimation {
  // Main animation.
  PointZoomAnimationParameters animationParam = PointZoomAnimationParameters{
      .direction =
          PointZoomAnimationParameters::AnimationDirection::kContracting,
      .destinationFrame = _tabGridCellItem.originalFrame,
      .destinationCornerRadius = _tabGridCellItem.view.layer.cornerRadius};
  id<TabGridTransitionAnimation> mainAnimation =
      [[PointZoomTransitionAnimation alloc]
                 initWithView:_BVCContainerViewController.view
          animationParameters:animationParam];

  // Combine animation.
  id<TabGridTransitionAnimation> combinedIntroAndMainAnimations =
      [[TabGridTransitionAnimationGroup alloc]
          initWithAnimations:@[ mainAnimation ]];
  return combinedIntroAndMainAnimations;
}

// Returns TabGrid to Browser normal motion animation.
// TODO(crbug.com/40287759): Finish this animation.
- (id<TabGridTransitionAnimation>)tabGridToBrowserNormalAnimation {
  // Set the frame to be the same as the active cell.
  _BVCContainerViewController.view.frame = _tabGridCellItem.originalFrame;

  // Set the frame to be the same as the active cell.
  PointZoomAnimationParameters animationParam = PointZoomAnimationParameters{
      .direction = PointZoomAnimationParameters::AnimationDirection::kExpanding,
      .destinationFrame = _tabGridViewController.view.bounds,
      .destinationCornerRadius = DeviceCornerRadius()};
  id<TabGridTransitionAnimation> mainAnimation =
      [[PointZoomTransitionAnimation alloc]
                 initWithView:_BVCContainerViewController.view
          animationParameters:animationParam];

  // Combine animation.
  id<TabGridTransitionAnimation> combinedIntroAndMainAnimations =
      [[TabGridTransitionAnimationGroup alloc]
          initWithAnimations:@[ mainAnimation ]];
  return combinedIntroAndMainAnimations;
}

// Returns Browser to TabGrid reduced motion animation.
- (id<TabGridTransitionAnimation>)browserToTabGridReducedMotionAnimation {
  return [[CenteredZoomTransitionAnimation alloc]
      initWithView:_BVCContainerViewController.view
         direction:CenteredZoomTransitionAnimationDirection::kContracting];
}

// Returns TabGrid to Browser reduced motion animation.
- (id<TabGridTransitionAnimation>)tabGridToBrowserReducedMotionAnimation {
  return [[CenteredZoomTransitionAnimation alloc]
      initWithView:_BVCContainerViewController.view
         direction:CenteredZoomTransitionAnimationDirection::kExpanding];
}

@end