Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#import <UIKit/UIKit.h>

#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"

Expand Down Expand Up @@ -154,10 +156,38 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {

- (void)showShareViewController:(NSString*)content {
UIViewController* engineViewController = [_engine.get() viewController];

NSArray* itemsToShare = @[ content ?: [NSNull null] ];
UIActivityViewController* activityViewController =
[[[UIActivityViewController alloc] initWithActivityItems:itemsToShare
applicationActivities:nil] autorelease];

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// On iPad, the share screen is presented in a popover view, and requires a
// sourceView and sourceRect
FlutterTextInputPlugin* _textInputPlugin = [_engine.get() textInputPlugin];
UITextRange* range = _textInputPlugin.textInputView.selectedTextRange;

// firstRectForRange cannot be used here as it's current implementation does
// not always return the full rect of the range.
CGRect firstRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
caretRectForPosition:(FlutterTextPosition*)range.start];
CGRect transformedFirstRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
localRectFromFrameworkTransform:firstRect];
CGRect lastRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
caretRectForPosition:(FlutterTextPosition*)range.end];
CGRect transformedLastRect = [(FlutterTextInputView*)_textInputPlugin.textInputView
localRectFromFrameworkTransform:lastRect];

activityViewController.popoverPresentationController.sourceView = engineViewController.view;
// In case of RTL Language, get the minimum x coordinate
activityViewController.popoverPresentationController.sourceRect =
CGRectMake(fmin(transformedFirstRect.origin.x, transformedLastRect.origin.x),
transformedFirstRect.origin.y,
abs(transformedLastRect.origin.x - transformedFirstRect.origin.x),
transformedFirstRect.size.height);
}

[engineViewController presentViewController:activityViewController animated:YES completion:nil];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,43 @@ - (void)testShareScreenInvoked {
[self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testShareScreenInvokedOnIPad {
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];
[engine runWithEntrypoint:nil];
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(engine);

XCTestExpectation* presentExpectation =
[self expectationWithDescription:@"Share view controller presented on iPad"];

FlutterViewController* engineViewController =
[[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil] autorelease];
FlutterViewController* mockEngineViewController = OCMPartialMock(engineViewController);
OCMStub([mockEngineViewController
presentViewController:[OCMArg isKindOfClass:[UIActivityViewController class]]
animated:YES
completion:nil]);

id mockTraitCollection = OCMClassMock([UITraitCollection class]);
OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);

FlutterPlatformPlugin* plugin =
[[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()] autorelease];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);

FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Share.invoke"
arguments:@"Test"];
FlutterResult result = ^(id result) {
OCMVerify([mockEngineViewController
presentViewController:[OCMArg isKindOfClass:[UIActivityViewController class]]
animated:YES
completion:nil]);
[presentExpectation fulfill];
};
[mockPlugin handleMethodCall:methodCall result:result];
[self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testClipboardHasCorrectStrings {
[UIPasteboard generalPasteboard].string = nil;
FlutterEngine* engine = [[[FlutterEngine alloc] initWithName:@"test" project:nil] autorelease];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ FLUTTER_DARWIN_EXPORT
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
- (instancetype)initWithOwner:(FlutterTextInputPlugin*)textInputPlugin NS_DESIGNATED_INITIALIZER;

// TODO(louisehsu): These are being exposed to support Share in FlutterPlatformPlugin
// Consider moving that feature into FlutterTextInputPlugin to avoid exposing extra methods
- (CGRect)localRectFromFrameworkTransform:(CGRect)incomingRect;
- (CGRect)caretRectForPosition:(UITextPosition*)position;
@end

@interface UIView (FindFirstResponder)
Expand Down