/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2020 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 6 End-User License Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). End User License Agreement: www.juce.com/juce-6-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl, private Component { public: ContentSharerNativeImpl (ContentSharer& cs) : owner (cs) { static PopoverDelegateClass cls; popoverDelegate.reset ([cls.createInstance() init]); } ~ContentSharerNativeImpl() override { exitModalState (0); } void shareFiles (const Array& files) override { auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) files.size()]; for (const auto& f : files) { NSString* nativeFilePath = nil; if (f.isLocalFile()) { nativeFilePath = juceStringToNS (f.getLocalFile().getFullPathName()); } else { auto filePath = f.toString (false); auto* fileDirectory = filePath.contains ("/") ? juceStringToNS (filePath.upToLastOccurrenceOf ("/", false, false)) : [NSString string]; auto fileName = juceStringToNS (filePath.fromLastOccurrenceOf ("/", false, false) .upToLastOccurrenceOf (".", false, false)); auto fileExt = juceStringToNS (filePath.fromLastOccurrenceOf (".", false, false)); if ([fileDirectory length] == NSUInteger (0)) nativeFilePath = [[NSBundle mainBundle] pathForResource: fileName ofType: fileExt]; else nativeFilePath = [[NSBundle mainBundle] pathForResource: fileName ofType: fileExt inDirectory: fileDirectory]; } if (nativeFilePath != nil) [urls addObject: [NSURL fileURLWithPath: nativeFilePath]]; } share (urls); } void shareText (const String& text) override { auto array = [NSArray arrayWithObject: juceStringToNS (text)]; share (array); } private: void share (NSArray* items) { if ([items count] == 0) { jassertfalse; owner.sharingFinished (false, "No valid items found for sharing."); return; } controller.reset ([[UIActivityViewController alloc] initWithActivityItems: items applicationActivities: nil]); controller.get().excludedActivityTypes = nil; controller.get().completionWithItemsHandler = ^ (UIActivityType type, BOOL completed, NSArray* returnedItems, NSError* error) { ignoreUnused (type); ignoreUnused (returnedItems); succeeded = completed; if (error != nil) errorDescription = nsStringToJuce ([error localizedDescription]); exitModalState (0); }; controller.get().modalTransitionStyle = UIModalTransitionStyleCoverVertical; auto bounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea; setBounds (bounds); setAlwaysOnTop (true); setVisible (true); addToDesktop (0); enterModalState (true, ModalCallbackFunction::create ([this] (int) { owner.sharingFinished (succeeded, errorDescription); }), false); } static bool isIPad() { return [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad; } //============================================================================== void parentHierarchyChanged() override { auto* newPeer = dynamic_cast (getPeer()); if (peer != newPeer) { peer = newPeer; if (isIPad()) { controller.get().preferredContentSize = peer->view.frame.size; auto screenBounds = [UIScreen mainScreen].bounds; auto* popoverController = controller.get().popoverPresentationController; popoverController.sourceView = peer->view; popoverController.sourceRect = CGRectMake (0.f, screenBounds.size.height - 10.f, screenBounds.size.width, 10.f); popoverController.canOverlapSourceViewRect = YES; popoverController.delegate = popoverDelegate.get(); } if (auto* parentController = peer->controller) [parentController showViewController: controller.get() sender: parentController]; } } //============================================================================== struct PopoverDelegateClass : public ObjCClass> { PopoverDelegateClass() : ObjCClass> ("PopoverDelegateClass_") { addMethod (@selector (popoverPresentationController:willRepositionPopoverToRect:inView:), willRepositionPopover, "v@:@@@"); registerClass(); } //============================================================================== static void willRepositionPopover (id, SEL, UIPopoverPresentationController*, CGRect* rect, UIView*) { auto screenBounds = [UIScreen mainScreen].bounds; rect->origin.x = 0.f; rect->origin.y = screenBounds.size.height - 10.f; rect->size.width = screenBounds.size.width; rect->size.height = 10.f; } }; ContentSharer& owner; UIViewComponentPeer* peer = nullptr; std::unique_ptr controller; std::unique_ptr, NSObjectDeleter> popoverDelegate; bool succeeded = false; String errorDescription; }; //============================================================================== ContentSharer::Pimpl* ContentSharer::createPimpl() { return new ContentSharerNativeImpl (*this); } } // namespace juce