The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
7.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #if JUCE_IOS || JUCE_ANDROID
  22. //==============================================================================
  23. class ContentSharer::PrepareImagesThread : private Thread
  24. {
  25. public:
  26. PrepareImagesThread (ContentSharer& cs, const Array<Image>& imagesToUse,
  27. ImageFileFormat* imageFileFormatToUse)
  28. : Thread ("ContentSharer::PrepareImagesThread"),
  29. owner (cs),
  30. images (imagesToUse),
  31. imageFileFormat (imageFileFormatToUse == nullptr ? new PNGImageFormat()
  32. : imageFileFormatToUse),
  33. extension (imageFileFormat->getFormatName().toLowerCase())
  34. {
  35. startThread();
  36. }
  37. ~PrepareImagesThread()
  38. {
  39. signalThreadShouldExit();
  40. waitForThreadToExit (10000);
  41. }
  42. private:
  43. void run() override
  44. {
  45. for (const auto& image : images)
  46. {
  47. if (threadShouldExit())
  48. return;
  49. File tempFile = File::createTempFile (extension);
  50. if (! tempFile.create().wasOk())
  51. break;
  52. std::unique_ptr<FileOutputStream> outputStream (tempFile.createOutputStream());
  53. if (outputStream == nullptr)
  54. break;
  55. if (imageFileFormat->writeImageToStream (image, *outputStream))
  56. owner.temporaryFiles.add (tempFile);
  57. }
  58. finish();
  59. }
  60. void finish()
  61. {
  62. MessageManager::callAsync ([this] () { owner.filesToSharePrepared(); });
  63. }
  64. ContentSharer& owner;
  65. const Array<Image> images;
  66. std::unique_ptr<ImageFileFormat> imageFileFormat;
  67. String extension;
  68. };
  69. //==============================================================================
  70. class ContentSharer::PrepareDataThread : private Thread
  71. {
  72. public:
  73. PrepareDataThread (ContentSharer& cs, const MemoryBlock& mb)
  74. : Thread ("ContentSharer::PrepareDataThread"),
  75. owner (cs),
  76. data (mb)
  77. {
  78. startThread();
  79. }
  80. ~PrepareDataThread()
  81. {
  82. signalThreadShouldExit();
  83. waitForThreadToExit (10000);
  84. }
  85. private:
  86. void run() override
  87. {
  88. File tempFile = File::createTempFile ("data");
  89. if (tempFile.create().wasOk())
  90. {
  91. if (auto outputStream = std::unique_ptr<FileOutputStream> (tempFile.createOutputStream()))
  92. {
  93. size_t pos = 0;
  94. size_t totalSize = data.getSize();
  95. while (pos < totalSize)
  96. {
  97. if (threadShouldExit())
  98. return;
  99. size_t numToWrite = std::min ((size_t) 8192, totalSize - pos);
  100. outputStream->write (data.begin() + pos, numToWrite);
  101. pos += numToWrite;
  102. }
  103. owner.temporaryFiles.add (tempFile);
  104. }
  105. }
  106. finish();
  107. }
  108. void finish()
  109. {
  110. MessageManager::callAsync ([this] () { owner.filesToSharePrepared(); });
  111. }
  112. ContentSharer& owner;
  113. const MemoryBlock data;
  114. };
  115. #endif
  116. //==============================================================================
  117. JUCE_IMPLEMENT_SINGLETON (ContentSharer)
  118. ContentSharer::ContentSharer() {}
  119. ContentSharer::~ContentSharer() { clearSingletonInstance(); }
  120. void ContentSharer::shareFiles (const Array<URL>& files,
  121. std::function<void (bool, const String&)> callbackToUse)
  122. {
  123. #if JUCE_IOS || JUCE_ANDROID
  124. startNewShare (callbackToUse);
  125. pimpl->shareFiles (files);
  126. #else
  127. ignoreUnused (files);
  128. // Content sharing is not available on this platform!
  129. jassertfalse;
  130. if (callbackToUse)
  131. callbackToUse (false, "Content sharing is not available on this platform!");
  132. #endif
  133. }
  134. #if JUCE_IOS || JUCE_ANDROID
  135. void ContentSharer::startNewShare (std::function<void (bool, const String&)> callbackToUse)
  136. {
  137. // You should not start another sharing operation before the previous one is finished.
  138. // Forcibly stopping a previous sharing operation is rarely a good idea!
  139. jassert (pimpl == nullptr);
  140. pimpl.reset();
  141. prepareDataThread = nullptr;
  142. prepareImagesThread = nullptr;
  143. deleteTemporaryFiles();
  144. // You need to pass a valid callback.
  145. jassert (callbackToUse);
  146. callback = std::move (callbackToUse);
  147. pimpl.reset (createPimpl());
  148. }
  149. #endif
  150. void ContentSharer::shareText (const String& text,
  151. std::function<void (bool, const String&)> callbackToUse)
  152. {
  153. #if JUCE_IOS || JUCE_ANDROID
  154. startNewShare (callbackToUse);
  155. pimpl->shareText (text);
  156. #else
  157. ignoreUnused (text);
  158. // Content sharing is not available on this platform!
  159. jassertfalse;
  160. if (callbackToUse)
  161. callbackToUse (false, "Content sharing is not available on this platform!");
  162. #endif
  163. }
  164. void ContentSharer::shareImages (const Array<Image>& images,
  165. std::function<void (bool, const String&)> callbackToUse,
  166. ImageFileFormat* imageFileFormatToUse)
  167. {
  168. #if JUCE_IOS || JUCE_ANDROID
  169. startNewShare (callbackToUse);
  170. prepareImagesThread.reset (new PrepareImagesThread (*this, images, imageFileFormatToUse));
  171. #else
  172. ignoreUnused (images, imageFileFormatToUse);
  173. // Content sharing is not available on this platform!
  174. jassertfalse;
  175. if (callbackToUse)
  176. callbackToUse (false, "Content sharing is not available on this platform!");
  177. #endif
  178. }
  179. #if JUCE_IOS || JUCE_ANDROID
  180. void ContentSharer::filesToSharePrepared()
  181. {
  182. Array<URL> urls;
  183. for (const auto& tempFile : temporaryFiles)
  184. urls.add (URL (tempFile));
  185. prepareImagesThread = nullptr;
  186. prepareDataThread = nullptr;
  187. pimpl->shareFiles (urls);
  188. }
  189. #endif
  190. void ContentSharer::shareData (const MemoryBlock& mb,
  191. std::function<void (bool, const String&)> callbackToUse)
  192. {
  193. #if JUCE_IOS || JUCE_ANDROID
  194. startNewShare (callbackToUse);
  195. prepareDataThread.reset (new PrepareDataThread (*this, mb));
  196. #else
  197. ignoreUnused (mb);
  198. if (callbackToUse)
  199. callbackToUse (false, "Content sharing not available on this platform!");
  200. #endif
  201. }
  202. void ContentSharer::sharingFinished (bool succeeded, const String& errorDescription)
  203. {
  204. deleteTemporaryFiles();
  205. std::function<void (bool, String)> cb;
  206. std::swap (cb, callback);
  207. String error (errorDescription);
  208. #if JUCE_IOS || JUCE_ANDROID
  209. pimpl.reset();
  210. #endif
  211. if (cb)
  212. cb (succeeded, error);
  213. }
  214. void ContentSharer::deleteTemporaryFiles()
  215. {
  216. for (auto& f : temporaryFiles)
  217. f.deleteFile();
  218. temporaryFiles.clear();
  219. }
  220. } // namespace juce