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