Merge branch 'release-candidate' into stable
diff --git a/.kokoro b/.kokoro index 8d0dff6..d820c6d 100755 --- a/.kokoro +++ b/.kokoro
@@ -1,4 +1,18 @@ #!/bin/bash +# +# Copyright 2017-present The Material Motion Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # Fail on any error. set -e @@ -6,30 +20,36 @@ # Display commands to stderr. set -x +KOKORO_RUNNER_VERSION="v3.*" + +fix_bazel_imports() { + if [ -z "$KOKORO_BUILD_NUMBER" ]; then + repo_prefix="" + else + repo_prefix="github/repo/" + fi + + # Fixes a bug in bazel where objc_library targets have a _ prefix. + find "${repo_prefix}tests/unit" -type f -name '*.swift' -exec sed -i '' -E "s/import Motion(.+)/import _Motion\1/" {} + || true + stashed_dir=$(pwd) + reset_imports() { + # Undoes our source changes from above. + find "${stashed_dir}/${tests_dir_prefix}tests/unit" -type f -name '*.swift' -exec sed -i '' -E "s/import _Motion(.+)/import Motion\1/" {} + || true + } + trap reset_imports EXIT +} + if [ ! -d .kokoro-ios-runner ]; then git clone https://github.com/material-foundation/kokoro-ios-runner.git .kokoro-ios-runner fi pushd .kokoro-ios-runner git fetch > /dev/null -TAG=$(git tag -l "v3*" | sort | tail -n1) -git checkout $TAG > /dev/null +TAG=$(git tag --sort=v:refname -l "$KOKORO_RUNNER_VERSION" | tail -n1) +git checkout "$TAG" > /dev/null popd -if [ -z "$KOKORO_BUILD_NUMBER" ]; then - tests_dir_prefix="" -else - tests_dir_prefix="github/repo/" -fi - -# Fixes a bug in bazel where objc_library targets have a _ prefix. -find ${tests_dir_prefix}tests/unit -type f -name '*.swift' -exec sed -i '' -E "s/import Motion(.+)/import _Motion\1/" {} + || true -stashed_dir=$(pwd) -reset_imports() { - # Undoes our source changes from above. - find ${stashed_dir}/${tests_dir_prefix}tests/unit -type f -name '*.swift' -exec sed -i '' -E "s/import _Motion(.+)/import Motion\1/" {} + || true -} -trap reset_imports EXIT +fix_bazel_imports ./.kokoro-ios-runner/bazel.sh test //:UnitTests 8.1.0
diff --git a/BUILD b/BUILD index 6c9774f..30ee3e5 100644 --- a/BUILD +++ b/BUILD
@@ -1,12 +1,28 @@ +# Copyright 2017-present The Material Motion Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# # Description: # Motion interchange format. +load("@bazel_ios_warnings//:strict_warnings_objc_library.bzl", "strict_warnings_objc_library") +load("@build_bazel_rules_apple//apple:swift.bzl", "swift_library") +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") + licenses(["notice"]) # Apache 2.0 exports_files(["LICENSE"]) -load("@bazel_ios_warnings//:strict_warnings_objc_library.bzl", "strict_warnings_objc_library") - strict_warnings_objc_library( name = "MotionInterchange", srcs = glob([ @@ -22,8 +38,6 @@ visibility = ["//visibility:public"], ) -load("@build_bazel_rules_apple//apple:swift.bzl", "swift_library") - swift_library( name = "UnitTestsSwiftLib", srcs = glob([ @@ -42,8 +56,6 @@ visibility = ["//visibility:private"], ) -load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") - ios_unit_test( name = "UnitTests", deps = [
diff --git a/CHANGELOG.md b/CHANGELOG.md index f4ac5c5..c25b775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,3 +1,29 @@ +# 1.2.0 + +This minor release introduces a new API for reversing cubic beziers and a unit test for +`MDMModalMovementTiming`. + +## New features + +`MDMMotionCurveReversedBezier` reverses cubic bezier curves. Intended for use when building mirrored +bi-directional transitions. + +## Source changes + +* [Add a unit test for MDMModalMovementTiming. (#12)](https://github.com/material-motion/motion-interchange-objc/commit/a0c3566ad52a45365657e0591701afa7989eb822) (featherless) +* [Add MDMMotionCurveReversed for reversing timing curves. (#11)](https://github.com/material-motion/motion-interchange-objc/commit/a54a5ffa49052a198b4bb5beedce737bb61ebc91) (featherless) + +## API changes + +### MDMMotionCurveReversedBezier + +**new** function: `MDMMotionCurveReversedBezier`. + +## Non-source changes + +* [Standardize the kokoro and bazel files. (#13)](https://github.com/material-motion/motion-interchange-objc/commit/a009d3f7d08d8b2d087891a86eb1e298714198b4) (featherless) +* [Use the v1.0.0 tag for bazel_ios_warnings. (#10)](https://github.com/material-motion/motion-interchange-objc/commit/545b6a448ddb235279318dc262f051d653a48ed4) (featherless) + # 1.1.1 This patch release migrates the project's continuous integration pipeline from arc to bazel and
diff --git a/MotionInterchange.podspec b/MotionInterchange.podspec index 7d96972..5d5cc55 100644 --- a/MotionInterchange.podspec +++ b/MotionInterchange.podspec
@@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MotionInterchange" s.summary = "Motion interchange format." - s.version = "1.1.1" + s.version = "1.2.0" s.authors = "The Material Motion Authors" s.license = "Apache 2.0" s.homepage = "https://github.com/material-motion/motion-interchange-objc"
diff --git a/Podfile.lock b/Podfile.lock index 734cbe9..ef0bf5f 100644 --- a/Podfile.lock +++ b/Podfile.lock
@@ -1,6 +1,6 @@ PODS: - CatalogByConvention (2.1.1) - - MotionInterchange (1.1.1) + - MotionInterchange (1.2.0) DEPENDENCIES: - CatalogByConvention @@ -12,7 +12,7 @@ SPEC CHECKSUMS: CatalogByConvention: c3a5319de04250a7cd4649127fcfca5fe3322a43 - MotionInterchange: 3556642f403549449a49f05653da3bd932a5e072 + MotionInterchange: 499c98e7628a8a078905749734dbfedbfae54cca PODFILE CHECKSUM: 09090d12db5aab00a13fe82da94f338ebd03f5dc
diff --git a/WORKSPACE b/WORKSPACE index c01a93d..722a7a4 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -1,3 +1,17 @@ +# Copyright 2017-present The Material Motion Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + git_repository( name = "build_bazel_rules_apple", remote = "https://github.com/bazelbuild/rules_apple.git", @@ -7,5 +21,5 @@ git_repository( name = "bazel_ios_warnings", remote = "https://github.com/material-foundation/bazel_ios_warnings.git", - commit = "3e61cb5b60f52c8b9c77b5d62364d8b4d25e528f", + tag = "v1.0.1", )
diff --git a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj index 29dcd40..c0139ca 100644 --- a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj +++ b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 6619E1D91FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */; }; 663ED7C51EDF1F0C0096B2A9 /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C01EDF1F0C0096B2A9 /* ExampleViewController.swift */; }; 663ED7C61EDF1F0C0096B2A9 /* ExampleViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C11EDF1F0C0096B2A9 /* ExampleViews.swift */; }; 663ED7C71EDF1F0C0096B2A9 /* HexColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C21EDF1F0C0096B2A9 /* HexColor.swift */; }; @@ -46,6 +47,7 @@ 09CEA5DEA01BA723D08D84E6 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "../../../Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; }; 2DE76D4D35953D836F578CDE /* Pods-MotionInterchangeCatalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MotionInterchangeCatalog.debug.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MotionInterchangeCatalog/Pods-MotionInterchangeCatalog.debug.xcconfig"; sourceTree = "<group>"; }; 4AAB8EBB088513D48896641A /* Pods-MotionInterchangeCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MotionInterchangeCatalog.release.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MotionInterchangeCatalog/Pods-MotionInterchangeCatalog.release.xcconfig"; sourceTree = "<group>"; }; + 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MDMModalMovementTimingTests.m; sourceTree = "<group>"; }; 663ED7C01EDF1F0C0096B2A9 /* ExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = "<group>"; }; 663ED7C11EDF1F0C0096B2A9 /* ExampleViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViews.swift; sourceTree = "<group>"; }; 663ED7C21EDF1F0C0096B2A9 /* HexColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HexColor.swift; sourceTree = "<group>"; }; @@ -162,6 +164,7 @@ children = ( 663ED8001EE628BA0096B2A9 /* MDMMotionCurveTests.swift */, 663ED8021EE6299A0096B2A9 /* MDMMotionCurveTests.m */, + 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */, ); name = tests; path = ../../../tests/unit; @@ -477,6 +480,7 @@ files = ( 663ED8031EE6299A0096B2A9 /* MDMMotionCurveTests.m in Sources */, 663ED8011EE628BA0096B2A9 /* MDMMotionCurveTests.swift in Sources */, + 6619E1D91FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };
diff --git a/src/MDMMotionCurve.h b/src/MDMMotionCurve.h index f93f808..6eace63 100644 --- a/src/MDMMotionCurve.h +++ b/src/MDMMotionCurve.h
@@ -95,6 +95,15 @@ // clang-format on /** + For cubic bezier curves, returns a reversed cubic bezier curve. For all other curve types, a copy + of the original curve is returned. + */ +// clang-format off +FOUNDATION_EXTERN MDMMotionCurve MDMMotionCurveReversedBezier(MDMMotionCurve motionCurve) + NS_SWIFT_NAME(MotionCurveReversedBezier(fromMotionCurve:)); +// clang-format on + +/** Named indices for the bezier motion curve's data array. */ typedef NS_ENUM(NSUInteger, MDMBezierMotionCurveDataIndex) {
diff --git a/src/MDMMotionCurve.m b/src/MDMMotionCurve.m index 111846c..7cb7b3c 100644 --- a/src/MDMMotionCurve.m +++ b/src/MDMMotionCurve.m
@@ -31,3 +31,14 @@ [timingFunction getControlPointAtIndex:2 values:pt2]; return MDMMotionCurveMakeBezier(pt1[0], pt1[1], pt2[0], pt2[1]); } + +MDMMotionCurve MDMMotionCurveReversedBezier(MDMMotionCurve motionCurve) { + MDMMotionCurve reversed = motionCurve; + if (motionCurve.type == MDMMotionCurveTypeBezier) { + reversed.data[0] = 1 - motionCurve.data[2]; + reversed.data[1] = 1 - motionCurve.data[3]; + reversed.data[2] = 1 - motionCurve.data[0]; + reversed.data[3] = 1 - motionCurve.data[1]; + } + return reversed; +}
diff --git a/tests/unit/MDMModalMovementTimingTests.m b/tests/unit/MDMModalMovementTimingTests.m new file mode 100644 index 0000000..f955a9a --- /dev/null +++ b/tests/unit/MDMModalMovementTimingTests.m
@@ -0,0 +1,87 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import <XCTest/XCTest.h> + +#import "MotionInterchange.h" + +@interface MDMModalMovementTimingTests : XCTestCase +@property(nonatomic, strong) UIWindow *window; +@end + +@interface ModalPresentationExtractionViewController : UIViewController +@property(nonatomic, strong) CAAnimation *presentationPositionAnimation; +@end + +@implementation ModalPresentationExtractionViewController + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + // We just want the first position key path animation that affects this view controller. + if (!self.presentationPositionAnimation) { + self.presentationPositionAnimation = [self.view.layer animationForKey:@"position"]; + } +} + +@end + +@implementation MDMModalMovementTimingTests + +- (void)setUp { + [super setUp]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self.window.rootViewController = [[UIViewController alloc] initWithNibName:nil bundle:nil]; + [self.window makeKeyAndVisible]; +} + +- (void)tearDown { + self.window = nil; + + [super tearDown]; +} + +- (void)testSystemModalMovementTimingCurveMatchesModalMovementTiming { + ModalPresentationExtractionViewController *presentedViewController = + [[ModalPresentationExtractionViewController alloc] initWithNibName:nil bundle:nil]; + XCTestExpectation *didComplete = [self expectationWithDescription:@"Animation completed"]; + [self.window.rootViewController presentViewController:presentedViewController + animated:YES + completion:^{ + [didComplete fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + + XCTAssertTrue([presentedViewController.presentationPositionAnimation + isKindOfClass:[CASpringAnimation class]]); + CASpringAnimation *springAnimation = + (CASpringAnimation *)presentedViewController.presentationPositionAnimation; + + MDMMotionTiming timing = MDMModalMovementTiming; + XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexMass], + springAnimation.mass, + 0.001); + XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexTension], + springAnimation.stiffness, + 0.001); + XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexFriction], + springAnimation.damping, + 0.001); +} + +@end
diff --git a/tests/unit/MDMMotionCurveTests.swift b/tests/unit/MDMMotionCurveTests.swift index eaf59b7..0f1fb27 100644 --- a/tests/unit/MDMMotionCurveTests.swift +++ b/tests/unit/MDMMotionCurveTests.swift
@@ -43,4 +43,23 @@ XCTAssertEqualWithAccuracy(curve.data.2, 0.3, accuracy: 0.001) // friction XCTAssertEqualWithAccuracy(curve.data.3, 0.0, accuracy: 0.001) } + + func testReversedBezierCurve() { + let curve = MotionCurveMakeBezier(p1x: 0.1, p1y: 0.2, p2x: 0.3, p2y: 0.4) + let reversed = MotionCurveReversedBezier(fromMotionCurve: curve) + XCTAssertEqualWithAccuracy(curve.data.0, 1 - reversed.data.2, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.1, 1 - reversed.data.3, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.2, 1 - reversed.data.0, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.3, 1 - reversed.data.1, accuracy: 0.001) + } + + func testReversingBezierCurveTwiceGivesSameResult() { + let curve = MotionCurveMakeBezier(p1x: 0.1, p1y: 0.2, p2x: 0.3, p2y: 0.4) + let reversed = MotionCurveReversedBezier(fromMotionCurve: curve) + let reversedAgain = MotionCurveReversedBezier(fromMotionCurve: reversed) + XCTAssertEqualWithAccuracy(curve.data.0, reversedAgain.data.0, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.1, reversedAgain.data.1, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.2, reversedAgain.data.2, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.data.3, reversedAgain.data.3, accuracy: 0.001) + } }