[camera] Fix coordinate rotation for setting focus- and exposure points on iOS (#4158)

diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index 1f30104..236cf96 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.1+5
+
+* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode). 
+
 ## 0.8.1+4
 
 * Silenced warnings that may occur during build when using a very
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m
new file mode 100644
index 0000000..ee43d3f
--- /dev/null
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m
@@ -0,0 +1,55 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@import camera;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+@interface FLTCam : NSObject <FlutterTexture,
+                              AVCaptureVideoDataOutputSampleBufferDelegate,
+                              AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y;
+@end
+
+@interface CameraExposureTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+@property(readonly, nonatomic) id mockDevice;
+@property(readonly, nonatomic) id mockUIDevice;
+@end
+
+@implementation CameraExposureTests
+
+- (void)setUp {
+  _camera = [[FLTCam alloc] init];
+  _mockDevice = OCMClassMock([AVCaptureDevice class]);
+  _mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
+}
+
+- (void)tearDown {
+  [_mockDevice stopMocking];
+  [_mockUIDevice stopMocking];
+}
+
+- (void)testSetExpsourePointWithResult_SetsExposurePointOfInterest {
+  // UI is currently in landscape left orientation
+  OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
+  // Exposure point of interest is supported
+  OCMStub([_mockDevice isExposurePointOfInterestSupported]).andReturn(true);
+  // Set mock device as the current capture device
+  [_camera setValue:_mockDevice forKey:@"captureDevice"];
+
+  // Run test
+  [_camera
+      setExposurePointWithResult:^void(id _Nullable result) {
+      }
+                               x:1
+                               y:1];
+
+  // Verify the focus point of interest has been set
+  OCMVerify([_mockDevice setExposurePointOfInterest:CGPointMake(1, 1)]);
+}
+
+@end
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
index 5d93bdf..27537e7 100644
--- a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
@@ -19,12 +19,13 @@
 
 - (void)applyFocusMode;
 - (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
+- (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y;
 @end
 
 @interface CameraFocusTests : XCTestCase
 @property(readonly, nonatomic) FLTCam *camera;
 @property(readonly, nonatomic) id mockDevice;
-
+@property(readonly, nonatomic) id mockUIDevice;
 @end
 
 @implementation CameraFocusTests
@@ -32,11 +33,12 @@
 - (void)setUp {
   _camera = [[FLTCam alloc] init];
   _mockDevice = OCMClassMock([AVCaptureDevice class]);
+  _mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
 }
 
 - (void)tearDown {
-  // Put teardown code here. This method is called after the invocation of each test method in the
-  // class.
+  [_mockDevice stopMocking];
+  [_mockUIDevice stopMocking];
 }
 
 - (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
@@ -117,4 +119,23 @@
   [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
 }
 
+- (void)testSetFocusPointWithResult_SetsFocusPointOfInterest {
+  // UI is currently in landscape left orientation
+  OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
+  // Focus point of interest is supported
+  OCMStub([_mockDevice isFocusPointOfInterestSupported]).andReturn(true);
+  // Set mock device as the current capture device
+  [_camera setValue:_mockDevice forKey:@"captureDevice"];
+
+  // Run test
+  [_camera
+      setFocusPointWithResult:^void(id _Nullable result) {
+      }
+                            x:1
+                            y:1];
+
+  // Verify the focus point of interest has been set
+  OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]);
+}
+
 @end
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
new file mode 100644
index 0000000..380f6e9
--- /dev/null
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
@@ -0,0 +1,49 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@import camera;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+@interface FLTCam : NSObject <FlutterTexture,
+                              AVCaptureVideoDataOutputSampleBufferDelegate,
+                              AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
+                                            x:(double)x
+                                            y:(double)y;
+
+@end
+
+@interface CameraUtilTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+
+@end
+
+@implementation CameraUtilTests
+
+- (void)setUp {
+  _camera = [[FLTCam alloc] init];
+}
+
+- (void)testGetCGPointForCoordsWithOrientation_ShouldRotateCoords {
+  CGPoint point;
+  point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeLeft x:1 y:1];
+  XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+                @"Resulting coordinates are invalid.");
+  point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortrait x:0 y:1];
+  XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+                @"Resulting coordinates are invalid.");
+  point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeRight x:0 y:0];
+  XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+                @"Resulting coordinates are invalid.");
+  point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortraitUpsideDown
+                                                    x:1
+                                                    y:0];
+  XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+                @"Resulting coordinates are invalid.");
+}
+
+@end
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index ebd5366..d88eb45 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -1030,6 +1030,31 @@
   [captureDevice unlockForConfiguration];
 }
 
+- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
+                                            x:(double)x
+                                            y:(double)y {
+  double oldX = x, oldY = y;
+  switch (orientation) {
+    case UIDeviceOrientationPortrait:  // 90 ccw
+      y = 1 - oldX;
+      x = oldY;
+      break;
+    case UIDeviceOrientationPortraitUpsideDown:  // 90 cw
+      x = 1 - oldY;
+      y = oldX;
+      break;
+    case UIDeviceOrientationLandscapeRight:  // 180
+      x = 1 - x;
+      y = 1 - y;
+      break;
+    case UIDeviceOrientationLandscapeLeft:
+    default:
+      // No rotation required
+      break;
+  }
+  return CGPointMake(x, y);
+}
+
 - (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
   if (!_captureDevice.isExposurePointOfInterestSupported) {
     result([FlutterError errorWithCode:@"setExposurePointFailed"
@@ -1037,8 +1062,11 @@
                                details:nil]);
     return;
   }
+  UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
   [_captureDevice lockForConfiguration:nil];
-  [_captureDevice setExposurePointOfInterest:CGPointMake(y, 1 - x)];
+  [_captureDevice setExposurePointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
+                                                                                    x:x
+                                                                                    y:y]];
   [_captureDevice unlockForConfiguration];
   // Retrigger auto exposure
   [self applyExposureMode];
@@ -1052,11 +1080,16 @@
                                details:nil]);
     return;
   }
+  UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
   [_captureDevice lockForConfiguration:nil];
-  [_captureDevice setFocusPointOfInterest:CGPointMake(y, 1 - x)];
+
+  [_captureDevice setFocusPointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
+                                                                                 x:x
+                                                                                 y:y]];
   [_captureDevice unlockForConfiguration];
   // Retrigger auto focus
   [self applyFocusMode];
+
   result(nil);
 }
 
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index 789910e..78eb49a 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
   and streaming image buffers to dart.
 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.8.1+4
+version: 0.8.1+5
 
 environment:
   sdk: ">=2.12.0 <3.0.0"