chromium/ios/chrome/browser/ui/fullscreen/fullscreen_animator.mm

// Copyright 2018 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/fullscreen/fullscreen_animator.h"

#import <math.h>
#import <algorithm>
#import <memory>

#import "base/check_op.h"
#import "ios/chrome/common/material_timing.h"
#import "ui/gfx/geometry/cubic_bezier.h"

CGFloat GetFinalFullscreenProgressForAnimation(FullscreenAnimatorStyle style) {
  return style == FullscreenAnimatorStyle::ENTER_FULLSCREEN ? 0.0 : 1.0;
}

@interface FullscreenAnimator () {
  // The bezier backing the timing curve.
  std::unique_ptr<gfx::CubicBezier> _bezier;
  // The current progress value that was recorded when the animator was stopped.
  CGFloat _progressUponStopping;
}
@end

@implementation FullscreenAnimator
@synthesize style = _style;
@synthesize startProgress = _startProgress;
@synthesize finalProgress = _finalProgress;

- (instancetype)initWithStartProgress:(CGFloat)startProgress
                                style:(FullscreenAnimatorStyle)style {
  // Control points for Material Design CurveEaseOut curve.
  UICubicTimingParameters* timingParams = [[UICubicTimingParameters alloc]
      initWithControlPoint1:CGPointMake(0.0, 0.0)
              controlPoint2:CGPointMake(0.2, 0.1)];
  DCHECK_GE(startProgress, 0.0);
  DCHECK_LE(startProgress, 1.0);
  self = [super initWithDuration:kMaterialDuration1
                timingParameters:timingParams];
  if (self) {
    DCHECK_GE(startProgress, 0.0);
    DCHECK_LE(startProgress, 1.0);
    _style = style;
    _startProgress = startProgress;
    _finalProgress = GetFinalFullscreenProgressForAnimation(_style);
    _bezier = std::make_unique<gfx::CubicBezier>(
        timingParams.controlPoint1.x, timingParams.controlPoint1.y,
        timingParams.controlPoint2.x, timingParams.controlPoint2.y);
  }
  return self;
}

#pragma mark Accessors

- (CGFloat)currentProgress {
  if (self.state == UIViewAnimatingStateStopped)
    return _progressUponStopping;
  CGFloat interpolationFraction = _bezier->Solve(self.fractionComplete);
  CGFloat range = self.finalProgress - self.startProgress;
  return self.startProgress + interpolationFraction * range;
}

#pragma mark Public

- (CGFloat)progressForAnimatingPosition:(UIViewAnimatingPosition)position {
  switch (position) {
    case UIViewAnimatingPositionStart:
      return self.startProgress;
    case UIViewAnimatingPositionEnd:
      return self.finalProgress;
    case UIViewAnimatingPositionCurrent:
      return self.currentProgress;
  }
}

#pragma mark UIViewAnimating

- (void)stopAnimation:(BOOL)withoutFinishing {
  // Record the progress value when transitioning from the active to stopped
  // state.  This allows `currentProgress` to return the correct value after
  // stopping, as `fractionComplete` is reset to 0.0 for stopped animators.
  if (self.state == UIViewAnimatingStateActive)
    _progressUponStopping = self.currentProgress;
  if (_progressUponStopping == _startProgress)
    return;
  [super stopAnimation:withoutFinishing];
}

#pragma mark UIViewPropertyAnimator

- (BOOL)isInterruptible {
  return YES;
}

@end