/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2017 - ROLI Ltd. You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. 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_CONTENT_SHARING //============================================================================== 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() override { 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() override { 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_CONTENT_SHARING 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_CONTENT_SHARING 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_CONTENT_SHARING 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_CONTENT_SHARING 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_CONTENT_SHARING 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_CONTENT_SHARING 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_CONTENT_SHARING pimpl.reset(); #endif if (cb) cb (succeeded, error); } void ContentSharer::deleteTemporaryFiles() { for (auto& f : temporaryFiles) f.deleteFile(); temporaryFiles.clear(); } } // namespace juce