/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. 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 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-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 { #if JUCE_IOS || JUCE_ANDROID //============================================================================== class ContentSharer::PrepareImagesThread : private Thread { public: PrepareImagesThread (ContentSharer& cs, const Array& imagesToUse, ImageFileFormat* imageFileFormatToUse) : Thread ("ContentSharer::PrepareImagesThread"), owner (cs), images (imagesToUse), imageFileFormat (imageFileFormatToUse == nullptr ? new PNGImageFormat() : imageFileFormatToUse), extension (imageFileFormat->getFormatName().toLowerCase()) { startThread(); } ~PrepareImagesThread() { signalThreadShouldExit(); waitForThreadToExit (10000); } private: void run() override { for (const auto& image : images) { if (threadShouldExit()) return; File tempFile = File::createTempFile (extension); if (! tempFile.create().wasOk()) break; std::unique_ptr outputStream (tempFile.createOutputStream()); if (outputStream == nullptr) break; if (imageFileFormat->writeImageToStream (image, *outputStream)) owner.temporaryFiles.add (tempFile); } finish(); } void finish() { MessageManager::callAsync ([this] () { owner.filesToSharePrepared(); }); } ContentSharer& owner; const Array images; std::unique_ptr imageFileFormat; String extension; }; //============================================================================== class ContentSharer::PrepareDataThread : private Thread { public: PrepareDataThread (ContentSharer& cs, const MemoryBlock& mb) : Thread ("ContentSharer::PrepareDataThread"), owner (cs), data (mb) { startThread(); } ~PrepareDataThread() { signalThreadShouldExit(); waitForThreadToExit (10000); } private: void run() override { File tempFile = File::createTempFile ("data"); if (tempFile.create().wasOk()) { if (auto outputStream = std::unique_ptr (tempFile.createOutputStream())) { size_t pos = 0; size_t totalSize = data.getSize(); while (pos < totalSize) { if (threadShouldExit()) return; size_t numToWrite = std::min ((size_t) 8192, totalSize - pos); outputStream->write (data.begin() + pos, numToWrite); pos += numToWrite; } owner.temporaryFiles.add (tempFile); } } finish(); } void finish() { MessageManager::callAsync ([this] () { owner.filesToSharePrepared(); }); } ContentSharer& owner; const MemoryBlock data; }; #endif //============================================================================== JUCE_IMPLEMENT_SINGLETON (ContentSharer) ContentSharer::ContentSharer() {} ContentSharer::~ContentSharer() { clearSingletonInstance(); } void ContentSharer::shareFiles (const Array& files, std::function callbackToUse) { #if JUCE_IOS || JUCE_ANDROID startNewShare (callbackToUse); pimpl->shareFiles (files); #else ignoreUnused (files); // Content sharing is not available on this platform! jassertfalse; if (callbackToUse) callbackToUse (false, "Content sharing is not available on this platform!"); #endif } #if JUCE_IOS || JUCE_ANDROID void ContentSharer::startNewShare (std::function callbackToUse) { // You should not start another sharing operation before the previous one is finished. // Forcibly stopping a previous sharing operation is rarely a good idea! jassert (pimpl == nullptr); pimpl.reset(); prepareDataThread = nullptr; prepareImagesThread = nullptr; deleteTemporaryFiles(); // You need to pass a valid callback. jassert (callbackToUse); callback = std::move (callbackToUse); pimpl.reset (createPimpl()); } #endif void ContentSharer::shareText (const String& text, std::function callbackToUse) { #if JUCE_IOS || JUCE_ANDROID startNewShare (callbackToUse); pimpl->shareText (text); #else ignoreUnused (text); // Content sharing is not available on this platform! jassertfalse; if (callbackToUse) callbackToUse (false, "Content sharing is not available on this platform!"); #endif } void ContentSharer::shareImages (const Array& images, std::function callbackToUse, ImageFileFormat* imageFileFormatToUse) { #if JUCE_IOS || JUCE_ANDROID startNewShare (callbackToUse); prepareImagesThread.reset (new PrepareImagesThread (*this, images, imageFileFormatToUse)); #else ignoreUnused (images, imageFileFormatToUse); // Content sharing is not available on this platform! jassertfalse; if (callbackToUse) callbackToUse (false, "Content sharing is not available on this platform!"); #endif } #if JUCE_IOS || JUCE_ANDROID void ContentSharer::filesToSharePrepared() { Array urls; for (const auto& tempFile : temporaryFiles) urls.add (URL (tempFile)); prepareImagesThread = nullptr; prepareDataThread = nullptr; pimpl->shareFiles (urls); } #endif void ContentSharer::shareData (const MemoryBlock& mb, std::function callbackToUse) { #if JUCE_IOS || JUCE_ANDROID startNewShare (callbackToUse); prepareDataThread.reset (new PrepareDataThread (*this, mb)); #else ignoreUnused (mb); if (callbackToUse) callbackToUse (false, "Content sharing not available on this platform!"); #endif } void ContentSharer::sharingFinished (bool succeeded, const String& errorDescription) { deleteTemporaryFiles(); std::function cb; std::swap (cb, callback); String error (errorDescription); #if JUCE_IOS || JUCE_ANDROID pimpl.reset(); #endif if (cb) cb (succeeded, error); } void ContentSharer::deleteTemporaryFiles() { for (auto& f : temporaryFiles) f.deleteFile(); temporaryFiles.clear(); } } // namespace juce