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. ScopedPointer<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. ScopedPointer<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. ScopedPointer<FileOutputStream> outputStream = tempFile.createOutputStream();
  92. if (outputStream != nullptr)
  93. {
  94. size_t pos = 0;
  95. size_t totalSize = data.getSize();
  96. while (pos < totalSize)
  97. {
  98. if (threadShouldExit())
  99. return;
  100. size_t numToWrite = std::min ((size_t) 8192, totalSize - pos);
  101. outputStream->write (data.begin() + pos, numToWrite);
  102. pos += numToWrite;
  103. }
  104. owner.temporaryFiles.add (tempFile);
  105. }
  106. }
  107. finish();
  108. }
  109. void finish()
  110. {
  111. MessageManager::callAsync ([this] () { owner.filesToSharePrepared(); });
  112. }
  113. ContentSharer& owner;
  114. const MemoryBlock data;
  115. };
  116. #endif
  117. //==============================================================================
  118. JUCE_IMPLEMENT_SINGLETON (ContentSharer)
  119. ContentSharer::ContentSharer() {}
  120. ContentSharer::~ContentSharer() { clearSingletonInstance(); }
  121. void ContentSharer::shareFiles (const Array<URL>& files,
  122. std::function<void (bool, const String&)> callbackToUse)
  123. {
  124. #if JUCE_IOS || JUCE_ANDROID
  125. startNewShare (callbackToUse);
  126. pimpl->shareFiles (files);
  127. #else
  128. ignoreUnused (files);
  129. // Content sharing is not available on this platform!
  130. jassertfalse;
  131. if (callbackToUse)
  132. callbackToUse (false, "Content sharing is not available on this platform!");
  133. #endif
  134. }
  135. #if JUCE_IOS || JUCE_ANDROID
  136. void ContentSharer::startNewShare (std::function<void (bool, const String&)> callbackToUse)
  137. {
  138. // You should not start another sharing operation before the previous one is finished.
  139. // Forcibly stopping a previous sharing operation is rarely a good idea!
  140. jassert (pimpl == nullptr);
  141. pimpl.reset();
  142. prepareDataThread = nullptr;
  143. prepareImagesThread = nullptr;
  144. deleteTemporaryFiles();
  145. // You need to pass a valid callback.
  146. jassert (callbackToUse);
  147. callback = static_cast<std::function<void (bool, const String&)>&&> (callbackToUse);
  148. pimpl.reset (createPimpl());
  149. }
  150. #endif
  151. void ContentSharer::shareText (const String& text,
  152. std::function<void (bool, const String&)> callbackToUse)
  153. {
  154. #if JUCE_IOS || JUCE_ANDROID
  155. startNewShare (callbackToUse);
  156. pimpl->shareText (text);
  157. #else
  158. ignoreUnused (text);
  159. // Content sharing is not available on this platform!
  160. jassertfalse;
  161. if (callbackToUse)
  162. callbackToUse (false, "Content sharing is not available on this platform!");
  163. #endif
  164. }
  165. void ContentSharer::shareImages (const Array<Image>& images,
  166. std::function<void (bool, const String&)> callbackToUse,
  167. ImageFileFormat* imageFileFormatToUse)
  168. {
  169. #if JUCE_IOS || JUCE_ANDROID
  170. startNewShare (callbackToUse);
  171. prepareImagesThread = new PrepareImagesThread (*this, images, imageFileFormatToUse);
  172. #else
  173. ignoreUnused (images, imageFileFormatToUse);
  174. // Content sharing is not available on this platform!
  175. jassertfalse;
  176. if (callbackToUse)
  177. callbackToUse (false, "Content sharing is not available on this platform!");
  178. #endif
  179. }
  180. #if JUCE_IOS || JUCE_ANDROID
  181. void ContentSharer::filesToSharePrepared()
  182. {
  183. Array<URL> urls;
  184. for (const auto& tempFile : temporaryFiles)
  185. urls.add (URL (tempFile));
  186. prepareImagesThread = nullptr;
  187. prepareDataThread = nullptr;
  188. pimpl->shareFiles (urls);
  189. }
  190. #endif
  191. void ContentSharer::shareData (const MemoryBlock& mb,
  192. std::function<void (bool, const String&)> callbackToUse)
  193. {
  194. #if JUCE_IOS || JUCE_ANDROID
  195. startNewShare (callbackToUse);
  196. prepareDataThread = new PrepareDataThread (*this, mb);
  197. #else
  198. ignoreUnused (mb);
  199. if (callbackToUse)
  200. callbackToUse (false, "Content sharing not available on this platform!");
  201. #endif
  202. }
  203. void ContentSharer::sharingFinished (bool succeeded, const String& errorDescription)
  204. {
  205. deleteTemporaryFiles();
  206. std::function<void (bool, String)> cb;
  207. std::swap (cb, callback);
  208. #if JUCE_IOS || JUCE_ANDROID
  209. pimpl.reset();
  210. #endif
  211. if (cb)
  212. cb (succeeded, errorDescription);
  213. }
  214. void ContentSharer::deleteTemporaryFiles()
  215. {
  216. for (auto& f : temporaryFiles)
  217. f.deleteFile();
  218. temporaryFiles.clear();
  219. }
  220. } // namespace juce