| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    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 7 End-User License
 -    Agreement and JUCE Privacy Policy.
 - 
 -    End User License Agreement: www.juce.com/juce-7-licence
 -    Privacy Policy: www.juce.com/juce-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
 - {
 - 
 - //==============================================================================
 - // This byte-code is generated from native/java/app/com/rmsl/juce/JuceContentProviderCursor.java with min sdk version 16
 - // See juce_core/native/java/README.txt on how to generate this byte-code.
 - static const uint8 javaJuceContentProviderCursor[] =
 - {31,139,8,8,191,114,161,94,0,3,106,97,118,97,74,117,99,101,67,111,110,116,101,110,116,80,114,111,118,105,100,101,114,67,117,
 - 114,115,111,114,46,100,101,120,0,117,147,177,111,211,64,20,198,223,157,157,148,150,54,164,192,208,14,64,144,16,18,67,235,138,2,
 - 75,40,162,10,44,150,65,149,2,25,218,233,176,173,198,37,241,69,182,19,121,96,160,21,136,37,19,98,234,80,85,149,152,88,24,248,
 - 3,24,146,63,130,141,137,129,13,169,99,7,190,203,157,33,18,194,210,207,247,222,229,189,239,157,206,95,130,48,159,91,91,191,75,227,
 - 60,200,143,134,239,247,151,62,189,43,175,127,249,246,235,241,215,241,112,231,231,193,237,135,22,81,143,136,242,214,157,139,
 - 100,158,99,78,84,37,189,95,2,159,129,13,70,128,129,83,179,127,102,242,27,120,157,129,71,224,16,156,128,143,96,12,126,128,69,232,
 - 93,6,75,224,10,184,14,238,129,13,224,130,16,188,4,3,174,245,44,51,79,205,152,53,171,101,206,86,54,241,27,20,206,152,120,136,
 - 248,156,137,63,32,134,12,45,76,206,166,187,148,230,28,169,125,62,201,249,159,156,209,188,201,23,77,93,241,187,122,134,38,40,225,
 - 52,42,124,197,245,252,94,141,104,147,182,113,95,21,76,208,83,222,114,125,86,89,101,168,109,162,162,183,134,46,86,249,71,215,
 - 158,228,54,149,239,71,113,148,61,32,230,210,85,183,239,135,13,25,103,97,156,109,37,114,16,5,97,210,232,39,169,76,86,247,196,64,
 - 208,53,79,196,65,34,163,192,9,68,38,94,136,52,116,158,136,44,137,114,93,84,167,91,158,47,187,78,210,77,59,206,30,164,156,255,
 - 234,213,137,181,136,183,92,178,90,174,135,192,163,75,59,158,154,225,116,68,188,235,52,33,26,239,214,169,228,119,100,26,210,121,
 - 95,118,250,221,248,169,232,134,41,45,251,90,176,217,22,73,33,215,80,101,1,217,109,153,102,52,171,222,207,228,115,52,218,89,
 - 59,74,233,38,191,48,63,83,217,88,161,85,194,178,141,139,224,184,28,190,255,218,30,113,126,192,201,98,223,249,130,185,27,54,181,
 - 22,222,227,83,254,43,60,49,50,235,180,15,11,47,150,167,252,200,106,186,95,121,146,85,255,122,134,215,180,190,242,169,101,106,
 - 212,119,165,154,238,157,124,243,170,142,213,255,224,55,143,234,50,200,64,3,0,0,0,0};
 - 
 - // This byte-code is generated from native/java/app/com/rmsl/juce/JuceContentProviderFileObserver.java with min sdk version 16
 - // See juce_core/native/java/README.txt on how to generate this byte-code.
 - static const uint8 javaJuceContentProviderFileObserver[] =
 - {31,139,8,8,194,122,161,94,0,3,106,97,118,97,74,117,99,101,67,111,110,116,101,110,116,80,114,111,118,105,100,101,114,70,105,
 - 108,101,79,98,115,101,114,118,101,114,46,100,101,120,0,133,147,205,107,19,65,24,198,223,249,72,98,171,46,105,235,69,16,201,65,81,
 - 68,221,136,10,66,84,144,250,65,194,130,197,212,32,5,15,155,100,104,182,38,187,97,119,141,241,32,126,30,196,147,23,79,246,216,
 - 131,120,202,77,169,80,212,191,64,193,66,143,30,60,138,255,130,62,179,51,165,219,147,129,223,188,239,188,239,204,179,179,179,79,
 - 186,106,60,93,61,123,158,54,159,255,248,112,97,210,120,124,98,237,251,177,7,109,245,115,253,225,198,159,47,243,171,135,198,130,
 - 104,72,68,227,214,185,89,178,191,45,78,116,128,76,189,8,62,3,169,235,128,129,61,204,204,203,204,204,171,24,142,99,207,2,226,
 - 4,124,4,159,192,6,248,5,254,130,42,250,87,193,13,224,129,91,224,14,184,11,30,129,23,224,21,120,3,222,130,53,240,158,27,125,110,
 - 159,95,176,231,41,233,51,216,249,75,44,152,178,249,107,228,211,54,95,69,190,215,230,239,144,11,40,57,153,150,200,222,81,100,
 - 170,166,190,47,139,68,51,185,200,237,93,8,27,191,218,66,17,138,186,54,225,230,44,195,42,209,149,194,18,238,206,201,58,250,121,
 - 235,182,215,172,160,191,200,137,159,113,172,158,204,246,50,251,62,38,151,89,103,251,29,139,23,131,48,72,47,19,171,19,107,208,
 - 145,198,253,142,154,143,194,84,133,233,66,28,141,130,174,138,175,7,125,117,179,157,168,120,164,226,211,43,254,200,167,131,158,
 - 31,118,227,40,232,186,81,226,230,219,53,114,189,78,52,112,227,65,210,119,87,32,229,254,71,175,70,179,158,150,116,251,126,184,
 - 236,54,211,56,8,151,107,196,90,36,90,117,143,100,171,97,70,175,142,2,134,195,29,35,213,236,249,241,110,161,107,35,148,169,160,
 - 178,32,123,81,146,210,148,30,23,163,219,137,34,57,240,147,123,84,138,66,179,76,14,253,180,71,50,237,5,9,29,21,229,185,153,146,
 - 115,233,20,157,228,206,92,201,89,194,21,113,70,156,61,125,34,191,113,246,12,223,143,253,198,101,237,183,223,133,229,226,182,103,
 - 121,206,183,34,231,93,153,243,111,129,118,60,92,164,29,31,179,138,217,175,189,204,202,102,141,246,24,175,24,125,237,111,97,
 - 215,104,15,80,197,236,205,252,81,54,185,254,255,252,3,243,31,208,130,120,3,0,0,0,0};
 - 
 - //==============================================================================
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  FIELD (authority, "authority", "Ljava/lang/String;")
 - 
 - DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  METHOD (constructor,       "<init>",            "(Landroid/os/ParcelFileDescriptor;JJ)V") \
 -  METHOD (createInputStream, "createInputStream", "()Ljava/io/FileInputStream;") \
 -  METHOD (getLength,         "getLength",         "()J")
 - 
 - DECLARE_JNI_CLASS (AssetFileDescriptor, "android/content/res/AssetFileDescriptor")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  METHOD (close, "close", "()V")
 - 
 - DECLARE_JNI_CLASS (JavaCloseable, "java/io/Closeable")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  STATICMETHOD (open, "open", "(Ljava/io/File;I)Landroid/os/ParcelFileDescriptor;")
 - 
 - DECLARE_JNI_CLASS (ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
 - #undef JNI_CLASS_MEMBERS
 - 
 - //==============================================================================
 - class AndroidContentSharerCursor
 - {
 - public:
 -     AndroidContentSharerCursor (JNIEnv* env,
 -                                 const LocalRef<jobject>& contentProvider,
 -                                 const LocalRef<jobjectArray>& resultColumns,
 -                                 std::function<void (AndroidContentSharerCursor&)> onCloseIn)
 -         : onClose (std::move (onCloseIn)),
 -           cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderCursor,
 -                                                                 JuceContentProviderCursor.constructor,
 -                                                                 reinterpret_cast<jlong> (this),
 -                                                                 resultColumns.get()))))
 -     {
 -         // the content provider must be created first
 -         jassert (contentProvider.get() != nullptr);
 -     }
 - 
 -     jobject getNativeCursor() const { return cursor.get(); }
 - 
 -     void addRow (LocalRef<jobjectArray>& values)
 -     {
 -         auto* env = getEnv();
 - 
 -         env->CallVoidMethod (cursor.get(), JuceContentProviderCursor.addRow, values.get());
 -     }
 - 
 - private:
 -     static void cursorClosed (JNIEnv*, AndroidContentSharerCursor& t)
 -     {
 -         MessageManager::callAsync ([&t]
 -                                    {
 -                                        NullCheckedInvocation::invoke (t.onClose, t);
 -                                    });
 -     }
 - 
 -     std::function<void (AndroidContentSharerCursor&)> onClose;
 -     GlobalRef cursor;
 - 
 -     //==============================================================================
 -     #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -      METHOD (addRow,      "addRow", "([Ljava/lang/Object;)V") \
 -      METHOD (constructor, "<init>", "(J[Ljava/lang/String;)V") \
 -      CALLBACK (generatedCallback<&AndroidContentSharerCursor::cursorClosed>, "contentSharerCursorClosed", "(J)V") \
 - 
 -     DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderCursor, "com/rmsl/juce/JuceContentProviderCursor", 16, javaJuceContentProviderCursor)
 -     #undef JNI_CLASS_MEMBERS
 - };
 - 
 - //==============================================================================
 - class AndroidContentSharerFileObserver
 - {
 - public:
 -     AndroidContentSharerFileObserver (JNIEnv* env,
 -                                       const LocalRef<jobject>& contentProvider,
 -                                       const File& filepathToUse,
 -                                       std::function<void()> onCloseIn)
 -         : onClose (std::move (onCloseIn)),
 -           filepath (filepathToUse),
 -           fileObserver (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderFileObserver,
 -                                                                       JuceContentProviderFileObserver.constructor,
 -                                                                       reinterpret_cast<jlong> (this),
 -                                                                       javaString (filepath.getFullPathName()).get(),
 -                                                                       open | access | closeWrite | closeNoWrite))))
 -     {
 -         // the content provider must be created first
 -         jassert (contentProvider.get() != nullptr);
 - 
 -         env->CallVoidMethod (fileObserver, JuceContentProviderFileObserver.startWatching);
 -     }
 - 
 -     void onFileEvent (int event, const LocalRef<jstring>&)
 -     {
 -         if (event == open)
 -         {
 -             ++numOpenedHandles;
 -         }
 -         else if (event == access)
 -         {
 -             fileWasRead = true;
 -         }
 -         else if (event == closeNoWrite || event == closeWrite)
 -         {
 -             --numOpenedHandles;
 - 
 -             // numOpenedHandles may get negative if we don't receive open handle event.
 -             if (fileWasRead && numOpenedHandles <= 0)
 -             {
 -                 MessageManager::callAsync ([fileObserver = fileObserver, onClose = onClose]
 -                 {
 -                     getEnv()->CallVoidMethod (fileObserver, JuceContentProviderFileObserver.stopWatching);
 -                     NullCheckedInvocation::invoke (onClose);
 -                 });
 -             }
 -         }
 -     }
 - 
 - private:
 -     static constexpr int open = 32;
 -     static constexpr int access = 1;
 -     static constexpr int closeWrite = 8;
 -     static constexpr int closeNoWrite = 16;
 - 
 -     std::function<void()> onClose;
 -     bool fileWasRead = false;
 -     int numOpenedHandles = 0;
 - 
 -     File filepath;
 -     GlobalRef fileObserver;
 - 
 -     //==============================================================================
 -     #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -      METHOD (constructor,   "<init>",        "(JLjava/lang/String;I)V") \
 -      METHOD (startWatching, "startWatching", "()V") \
 -      METHOD (stopWatching,  "stopWatching",  "()V") \
 -      CALLBACK (generatedCallback<&AndroidContentSharerFileObserver::onFileEventCallback>, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \
 - 
 -     DECLARE_JNI_CLASS_WITH_BYTECODE (JuceContentProviderFileObserver, "com/rmsl/juce/JuceContentProviderFileObserver", 16, javaJuceContentProviderFileObserver)
 -     #undef JNI_CLASS_MEMBERS
 - 
 -     static void onFileEventCallback (JNIEnv*, AndroidContentSharerFileObserver& t, jint event, jstring path)
 -     {
 -         t.onFileEvent (event, LocalRef<jstring> (path));
 -     }
 - };
 - 
 - //==============================================================================
 - class ContentSharerGlobalImpl
 - {
 - public:
 -     static ContentSharerGlobalImpl& getInstance()
 -     {
 -         static ContentSharerGlobalImpl result;
 -         return result;
 -     }
 - 
 -     const String packageName = juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (getAppContext().get(),
 -                                                                                                     AndroidContext.getPackageName)));
 -     const String uriBase = "content://" + packageName + ".sharingcontentprovider/";
 - 
 -     std::unique_ptr<ActivityLauncher> sharePreparedFiles (const std::map<String, File>& fileForUriIn,
 -                                                           const StringArray& mimeTypes,
 -                                                           std::function<void (bool)> callback)
 -     {
 -         // This function should be called from the main thread, but must not race with singleton
 -         // access from other threads.
 -         const ScopedLock lock { mutex };
 - 
 -         if (! isContentSharingEnabled())
 -         {
 -             // You need to enable "Content Sharing" in Projucer's Android exporter.
 -             jassertfalse;
 -             NullCheckedInvocation::invoke (callback, false);
 -             return {};
 -         }
 - 
 -         auto* env = getEnv();
 - 
 -         fileForUri.insert (fileForUriIn.begin(), fileForUriIn.end());
 - 
 -         LocalRef<jobject> fileUris (env->NewObject (JavaArrayList, JavaArrayList.constructor, fileForUriIn.size()));
 - 
 -         for (const auto& pair : fileForUriIn)
 -         {
 -             env->CallBooleanMethod (fileUris,
 -                                     JavaArrayList.add,
 -                                     env->CallStaticObjectMethod (AndroidUri,
 -                                                                  AndroidUri.parse,
 -                                                                  javaString (pair.first).get()));
 -         }
 - 
 -         LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructor));
 -         env->CallObjectMethod (intent,
 -                                AndroidIntent.setAction,
 -                                javaString ("android.intent.action.SEND_MULTIPLE").get());
 - 
 -         env->CallObjectMethod (intent,
 -                                AndroidIntent.setType,
 -                                javaString (getCommonMimeType (mimeTypes)).get());
 - 
 -         constexpr int grantReadPermission = 1;
 -         env->CallObjectMethod (intent, AndroidIntent.setFlags, grantReadPermission);
 - 
 -         env->CallObjectMethod (intent,
 -                                AndroidIntent.putParcelableArrayListExtra,
 -                                javaString ("android.intent.extra.STREAM").get(),
 -                                fileUris.get());
 - 
 -         return doIntent (intent, callback);
 -     }
 - 
 -     std::unique_ptr<ActivityLauncher> shareText (const String& text,
 -                                                  std::function<void (bool)> callback)
 -     {
 -         // This function should be called from the main thread, but must not race with singleton
 -         // access from other threads.
 -         const ScopedLock lock { mutex };
 - 
 -         if (! isContentSharingEnabled())
 -         {
 -             // You need to enable "Content Sharing" in Projucer's Android exporter.
 -             jassertfalse;
 -             NullCheckedInvocation::invoke (callback, false);
 -             return {};
 -         }
 - 
 -         auto* env = getEnv();
 - 
 -         LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructor));
 -         env->CallObjectMethod (intent,
 -                                AndroidIntent.setAction,
 -                                javaString ("android.intent.action.SEND").get());
 -         env->CallObjectMethod (intent,
 -                                AndroidIntent.putExtra,
 -                                javaString ("android.intent.extra.TEXT").get(),
 -                                javaString (text).get());
 -         env->CallObjectMethod (intent, AndroidIntent.setType, javaString ("text/plain").get());
 - 
 -         return doIntent (intent, callback);
 -     }
 - 
 -     static void onBroadcastResultReceive (JNIEnv*, jobject, int requestCode)
 -     {
 -         getInstance().sharingFinished (requestCode, true);
 -     }
 - 
 -     static jobject JNICALL contentSharerQuery (JNIEnv*, jobject contentProvider, jobject uri, jobjectArray projection)
 -     {
 -         return getInstance().query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
 -                                     LocalRef<jobject> (static_cast<jobject> (uri)),
 -                                     LocalRef<jobjectArray> (static_cast<jobjectArray> (projection)));
 -     }
 - 
 -     static jobject JNICALL contentSharerOpenFile (JNIEnv*, jobject contentProvider, jobject uri, jstring mode)
 -     {
 -         return getInstance().openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
 -                                        LocalRef<jobject> (static_cast<jobject> (uri)),
 -                                        LocalRef<jstring> (static_cast<jstring> (mode)));
 -     }
 - 
 -     static jobjectArray JNICALL contentSharerGetStreamTypes (JNIEnv*, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter)
 -     {
 -         return getInstance().getStreamTypes (addLocalRefOwner (uri),
 -                                              addLocalRefOwner (mimeTypeFilter));
 -     }
 - 
 - private:
 -     ContentSharerGlobalImpl() = default;
 - 
 -     LocalRef<jobject> makeChooser (const LocalRef<jobject>& intent, int request) const
 -     {
 -         auto* env = getEnv();
 - 
 -         const auto text = javaString ("Choose share target");
 - 
 -         if (getAndroidSDKVersion() < 22)
 -             return LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent,
 -                                                                    AndroidIntent.createChooser,
 -                                                                    intent.get(),
 -                                                                    text.get()));
 - 
 -         constexpr jint FLAG_UPDATE_CURRENT = 0x08000000;
 -         constexpr jint FLAG_IMMUTABLE = 0x04000000;
 - 
 -         const auto context = getAppContext();
 - 
 -         auto* klass = env->FindClass ("com/rmsl/juce/Receiver");
 -         const LocalRef<jobject> replyIntent (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), klass));
 -         getEnv()->CallObjectMethod (replyIntent, AndroidIntent.putExtraInt, javaString ("com.rmsl.juce.JUCE_REQUEST_CODE").get(), request);
 - 
 -         const auto flags = FLAG_UPDATE_CURRENT | (getAndroidSDKVersion() <= 23 ? 0 : FLAG_IMMUTABLE);
 -         const LocalRef<jobject> pendingIntent (env->CallStaticObjectMethod (AndroidPendingIntent,
 -                                                                             AndroidPendingIntent.getBroadcast,
 -                                                                             context.get(),
 -                                                                             request,
 -                                                                             replyIntent.get(),
 -                                                                             flags));
 - 
 -         return LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent22,
 -                                                                AndroidIntent22.createChooser,
 -                                                                intent.get(),
 -                                                                text.get(),
 -                                                                env->CallObjectMethod (pendingIntent,
 -                                                                                       AndroidPendingIntent.getIntentSender)));
 -     }
 - 
 -     //==============================================================================
 -     jobject openFile (const LocalRef<jobject>& contentProvider,
 -                       const LocalRef<jobject>& uri,
 -                       [[maybe_unused]] const LocalRef<jstring>& mode)
 -     {
 -         // This function can be called from multiple threads.
 -         const ScopedLock lock { mutex };
 - 
 -         auto* env = getEnv();
 - 
 -         auto uriElements = getContentUriElements (env, uri);
 - 
 -         if (uriElements.file == File())
 -             return nullptr;
 - 
 -         return getAssetFileDescriptor (env, contentProvider, uriElements.file);
 -     }
 - 
 -     jobject query (const LocalRef<jobject>& contentProvider,
 -                    const LocalRef<jobject>& uri,
 -                    const LocalRef<jobjectArray>& projection)
 -     {
 -         // This function can be called from multiple threads.
 -         const ScopedLock lock { mutex };
 - 
 -         StringArray requestedColumns = javaStringArrayToJuce (projection);
 -         StringArray supportedColumns = getSupportedColumns();
 - 
 -         StringArray resultColumns;
 - 
 -         for (const auto& col : supportedColumns)
 -         {
 -             if (requestedColumns.contains (col))
 -                 resultColumns.add (col);
 -         }
 - 
 -         // Unsupported columns were queried, file sharing may fail.
 -         if (resultColumns.isEmpty())
 -             return nullptr;
 - 
 -         auto resultJavaColumns = juceStringArrayToJava (resultColumns);
 - 
 -         auto* env = getEnv();
 - 
 -         const auto uriElements = getContentUriElements (env, uri);
 - 
 -         const auto callback = [info = uriElements.file] (auto& ref)
 -         {
 -             auto& pimplCursors = ContentSharerGlobalImpl::getInstance().cursors;
 -             const auto iter = std::lower_bound (pimplCursors.begin(), pimplCursors.end(), &ref, [] (const auto& managed, const auto* ptr)
 -             {
 -                 return managed.get() == ptr;
 -             });
 - 
 -             if (iter != pimplCursors.end() && iter->get() == &ref)
 -                 pimplCursors.erase (iter);
 -         };
 - 
 -         auto [iter, inserted] = cursors.emplace (new AndroidContentSharerCursor (env,
 -                                                                                  contentProvider,
 -                                                                                  resultJavaColumns,
 -                                                                                  callback));
 - 
 -         if (uriElements.file == File())
 -             return (*iter)->getNativeCursor();
 - 
 -         LocalRef<jobjectArray> values (env->NewObjectArray ((jsize) resultColumns.size(), JavaObject, nullptr));
 - 
 -         for (int i = 0; i < resultColumns.size(); ++i)
 -         {
 -             if (resultColumns.getReference (i) == "_display_name")
 -             {
 -                 env->SetObjectArrayElement (values, i, javaString (uriElements.filename).get());
 -             }
 -             else if (resultColumns.getReference (i) == "_size")
 -             {
 -                 LocalRef<jobject> javaFile (env->NewObject (JavaFile,
 -                                                             JavaFile.constructor,
 -                                                             javaString (uriElements.file.getFullPathName()).get()));
 - 
 -                 jlong fileLength = env->CallLongMethod (javaFile, JavaFile.length);
 - 
 -                 env->SetObjectArrayElement (values, i, env->NewObject (JavaLong, JavaLong.constructor, fileLength));
 -             }
 -         }
 - 
 -         (*iter)->addRow (values);
 -         return (*iter)->getNativeCursor();
 -     }
 - 
 -     jobjectArray getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
 -     {
 -         // This function can be called from multiple threads.
 -         const ScopedLock lock { mutex };
 - 
 -         auto* env = getEnv();
 - 
 -         auto extension = getContentUriElements (env, uri).filename.fromLastOccurrenceOf (".", false, true);
 - 
 -         if (extension.isEmpty())
 -             return nullptr;
 - 
 -         return juceStringArrayToJava (filterMimeTypes (detail::MimeTypeTable::getMimeTypesForFileExtension (extension),
 -                                                        juceString (mimeTypeFilter.get()))).release();
 -     }
 - 
 -     std::unique_ptr<ActivityLauncher> doIntent (const LocalRef<jobject>& intent,
 -                                                 std::function<void (bool)> callback)
 -     {
 -         static std::atomic<int> lastRequest = 1003;
 -         const auto requestCode = lastRequest++;
 -         callbackForRequest.emplace (requestCode, callback);
 -         const auto chooser = makeChooser (intent, requestCode);
 - 
 -         auto launcher = std::make_unique<ActivityLauncher> (chooser, requestCode);
 -         launcher->callback = [] (int request, int resultCode, LocalRef<jobject>)
 -         {
 -             ContentSharerGlobalImpl::getInstance().sharingFinished (request, resultCode == -1);
 -         };
 -         launcher->open();
 -         return launcher;
 -     }
 - 
 -     void sharingFinished (int request, bool succeeded)
 -     {
 -         // This function should be called from the main thread, but must not race with singleton
 -         // access from other threads.
 -         const ScopedLock lock { mutex };
 - 
 -         const auto iter = callbackForRequest.find (request);
 - 
 -         if (iter == callbackForRequest.end())
 -             return;
 - 
 -         const ScopeGuard scope { [&] { callbackForRequest.erase (iter); } };
 - 
 -         if (iter->second == nullptr)
 -             return;
 - 
 -         iter->second (succeeded);
 -     }
 - 
 -     bool isContentSharingEnabled() const
 -     {
 -         auto* env = getEnv();
 - 
 -         LocalRef<jobject> packageManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
 - 
 -         constexpr int getProviders = 8;
 -         LocalRef<jobject> packageInfo (env->CallObjectMethod (packageManager,
 -                                                               AndroidPackageManager.getPackageInfo,
 -                                                               javaString (packageName).get(),
 -                                                               getProviders));
 -         LocalRef<jobjectArray> providers  ((jobjectArray) env->GetObjectField (packageInfo,
 -                                                                                AndroidPackageInfo.providers));
 - 
 -         if (providers == nullptr)
 -             return false;
 - 
 -         auto sharingContentProviderAuthority = packageName + ".sharingcontentprovider";
 -         const int numProviders = env->GetArrayLength (providers.get());
 - 
 -         for (int i = 0; i < numProviders; ++i)
 -         {
 -             LocalRef<jobject> providerInfo (env->GetObjectArrayElement (providers, i));
 -             LocalRef<jstring> authority ((jstring) env->GetObjectField (providerInfo, AndroidProviderInfo.authority));
 - 
 -             if (juceString (authority) == sharingContentProviderAuthority)
 -                 return true;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     //==============================================================================
 -     struct ContentUriElements
 -     {
 -         String filename;
 -         File file;
 -     };
 - 
 -     ContentUriElements getContentUriElements (JNIEnv* env, const LocalRef<jobject>& uri) const
 -     {
 -         const auto fullUri = juceString ((jstring) env->CallObjectMethod (uri.get(), AndroidUri.toString));
 - 
 -         const auto filename = fullUri.fromLastOccurrenceOf ("/", false, true);
 - 
 -         const auto iter = fileForUri.find (fullUri);
 -         const auto info = iter != fileForUri.end() ? iter->second : File{};
 - 
 -         return { filename, info };
 -     }
 - 
 -     static StringArray getSupportedColumns()
 -     {
 -         return StringArray ("_display_name", "_size");
 -     }
 - 
 -     jobject getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider, const File& filepath)
 -     {
 -         if (nonAssetFilePathsPendingShare.find (filepath) == nonAssetFilePathsPendingShare.end())
 -         {
 -             const auto onCloseCallback = [filepath]
 -             {
 -                 ContentSharerGlobalImpl::getInstance().nonAssetFilePathsPendingShare.erase (filepath);
 -             };
 - 
 -             auto observer = rawToUniquePtr (new AndroidContentSharerFileObserver (env,
 -                                                                                   contentProvider,
 -                                                                                   filepath,
 -                                                                                   onCloseCallback));
 - 
 -             nonAssetFilePathsPendingShare.emplace (filepath, std::move (observer));
 -         }
 - 
 -         const LocalRef<jobject> javaFile (env->NewObject (JavaFile,
 -                                                           JavaFile.constructor,
 -                                                           javaString (filepath.getFullPathName()).get()));
 - 
 -         constexpr int modeReadOnly = 268435456;
 -         LocalRef<jobject> parcelFileDescriptor (env->CallStaticObjectMethod (ParcelFileDescriptor,
 -                                                                              ParcelFileDescriptor.open,
 -                                                                              javaFile.get(),
 -                                                                              modeReadOnly));
 - 
 -         if (jniCheckHasExceptionOccurredAndClear())
 -         {
 -             // Failed to create file descriptor. Have you provided a valid file path/resource name?
 -             jassertfalse;
 -             return nullptr;
 -         }
 - 
 -         jlong startOffset = 0;
 -         jlong unknownLength = -1;
 - 
 -         assetFileDescriptors.add (GlobalRef (LocalRef<jobject> (env->NewObject (AssetFileDescriptor,
 -                                                                                 AssetFileDescriptor.constructor,
 -                                                                                 parcelFileDescriptor.get(),
 -                                                                                 startOffset,
 -                                                                                 unknownLength))));
 - 
 -         return assetFileDescriptors.getReference (assetFileDescriptors.size() - 1).get();
 -     }
 - 
 -     StringArray filterMimeTypes (const StringArray& mimeTypes, const String& filter)
 -     {
 -         String filterToUse (filter.removeCharacters ("*"));
 - 
 -         if (filterToUse.isEmpty() || filterToUse == "/")
 -             return mimeTypes;
 - 
 -         StringArray result;
 - 
 -         for (const auto& type : mimeTypes)
 -             if (String (type).contains (filterToUse))
 -                 result.add (type);
 - 
 -         return result;
 -     }
 - 
 -     static String getCommonMimeType (const StringArray& mimeTypes)
 -     {
 -         if (mimeTypes.isEmpty())
 -             return "*/*";
 - 
 -         auto commonMime = mimeTypes[0];
 -         bool lookForCommonGroup = false;
 - 
 -         for (int i = 1; i < mimeTypes.size(); ++i)
 -         {
 -             if (mimeTypes[i] == commonMime)
 -                 continue;
 - 
 -             if (! lookForCommonGroup)
 -             {
 -                 lookForCommonGroup = true;
 -                 commonMime = commonMime.upToFirstOccurrenceOf ("/", true, false);
 -             }
 - 
 -             if (! mimeTypes[i].startsWith (commonMime))
 -                 return "*/*";
 -         }
 - 
 -         return lookForCommonGroup ? commonMime + "*" : commonMime;
 -     }
 - 
 -     CriticalSection mutex;
 -     Array<GlobalRef> assetFileDescriptors;
 -     std::map<File, std::unique_ptr<AndroidContentSharerFileObserver>> nonAssetFilePathsPendingShare;
 -     std::set<std::unique_ptr<AndroidContentSharerCursor>> cursors;
 -     std::map<String, File> fileForUri;
 -     std::map<int, std::function<void (bool)>> callbackForRequest;
 - };
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -      CALLBACK (ContentSharerGlobalImpl::contentSharerQuery,          "contentSharerQuery",          "(Landroid/net/Uri;[Ljava/lang/String;)Landroid/database/Cursor;") \
 -      CALLBACK (ContentSharerGlobalImpl::contentSharerOpenFile,       "contentSharerOpenFile",       "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;") \
 -      CALLBACK (ContentSharerGlobalImpl::contentSharerGetStreamTypes, "contentSharerGetStreamTypes", "(Landroid/net/Uri;Ljava/lang/String;)[Ljava/lang/String;") \
 - 
 - DECLARE_JNI_CLASS (JuceSharingContentProvider, "com/rmsl/juce/JuceSharingContentProvider")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -      CALLBACK (ContentSharerGlobalImpl::onBroadcastResultReceive, "onBroadcastResultNative", "(I)V")
 - 
 - DECLARE_JNI_CLASS (AndroidReceiver, "com/rmsl/juce/Receiver")
 - #undef JNI_CLASS_MEMBERS
 - 
 -     //==============================================================================
 - class AndroidContentSharerPrepareFilesTask final : private AsyncUpdater
 - {
 - public:
 -     AndroidContentSharerPrepareFilesTask (const Array<URL>& fileUrls,
 -                                           std::function<void (const std::map<String, File>&, const StringArray&)> onCompletionIn)
 -         : onCompletion (std::move (onCompletionIn)),
 -           task (std::async (std::launch::async, [this, fileUrls]
 -           {
 -               run (fileUrls);
 -               triggerAsyncUpdate();
 -           })) {}
 - 
 -     ~AndroidContentSharerPrepareFilesTask() override
 -     {
 -         task.wait();
 -         cancelPendingUpdate();
 -     }
 - 
 - private:
 -     const String packageName = ContentSharerGlobalImpl::getInstance().packageName;
 -     const String uriBase     = ContentSharerGlobalImpl::getInstance().uriBase;
 - 
 -     struct StreamCloser
 -     {
 -         explicit StreamCloser (const LocalRef<jobject>& streamToUse)
 -             : stream (GlobalRef (streamToUse))
 -         {
 -         }
 - 
 -         ~StreamCloser()
 -         {
 -             if (stream.get() != nullptr)
 -                 getEnv()->CallVoidMethod (stream, JavaCloseable.close);
 -         }
 - 
 -         GlobalRef stream;
 -     };
 - 
 -     void handleAsyncUpdate() override
 -     {
 -         onCompletion (infoForUri, mimeTypes);
 -     }
 - 
 -     void run (const Array<URL>& fileUrls)
 -     {
 -         auto* env = getEnv();
 - 
 -         StringArray filePaths;
 - 
 -         for (const auto& f : fileUrls)
 -         {
 -             const auto scheme = f.getScheme();
 - 
 -             // Only "file://" scheme or no scheme (for files in app bundle) are allowed!
 -             jassert (scheme.isEmpty() || scheme == "file");
 - 
 -             const auto fileToPrepare = [&]
 -             {
 -                 if (! scheme.isEmpty())
 -                     return f;
 - 
 -                 // Raw resource names need to be all lower case
 -                 jassert (f.toString (true).toLowerCase() == f.toString (true));
 - 
 -                 // This will get us a file with file:// URI
 -                 return copyAssetFileToTemporaryFile (env, f.toString (true));
 -             }();
 - 
 -             if (fileToPrepare.isEmpty())
 -                 continue;
 - 
 -             const auto filepath = URL::removeEscapeChars (fileToPrepare.toString (true).fromFirstOccurrenceOf ("file://", false, false));
 - 
 -             filePaths.add (filepath);
 -         }
 - 
 -         std::vector<String> extensions;
 - 
 -         for (const auto& filepath : filePaths)
 -         {
 -             const auto filename = filepath.fromLastOccurrenceOf ("/", false, true);
 -             extensions.push_back (filename.fromLastOccurrenceOf (".", false, true));
 -         }
 - 
 -         std::set<String> mimes;
 - 
 -         if (std::none_of (extensions.begin(), extensions.end(), [] (const String& s) { return s.isEmpty(); }))
 -             for (const auto& extension : extensions)
 -                 for (const auto& mime : detail::MimeTypeTable::getMimeTypesForFileExtension (extension))
 -                     mimes.insert (mime);
 - 
 -         for (const auto& mime : mimes)
 -             mimeTypes.add (mime);
 - 
 -         for (auto it = filePaths.begin(); it != filePaths.end(); ++it)
 -         {
 -             const auto filename = it->fromLastOccurrenceOf ("/", false, true);
 -             const auto contentString = uriBase + String (std::distance (filePaths.begin(), it)) + "/" + filename;
 -             infoForUri.emplace (contentString, *it);
 -         }
 -     }
 - 
 -     URL copyAssetFileToTemporaryFile (JNIEnv* env, const String& filename)
 -     {
 -         LocalRef<jobject> resources (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources));
 -         int fileId = env->CallIntMethod (resources,
 -                                          AndroidResources.getIdentifier,
 -                                          javaString (filename).get(),
 -                                          javaString ("raw").get(),
 -                                          javaString (packageName).get());
 - 
 -         // Raw resource not found. Please make sure that you include your file as a raw resource
 -         // and that you specify just the file name, without an extension.
 -         jassert (fileId != 0);
 - 
 -         if (fileId == 0)
 -             return {};
 - 
 -         LocalRef<jobject> assetFd (env->CallObjectMethod (resources,
 -                                                           AndroidResources.openRawResourceFd,
 -                                                           fileId));
 - 
 -         StreamCloser inputStream (LocalRef<jobject> (env->CallObjectMethod (assetFd, AssetFileDescriptor.createInputStream)));
 - 
 -         if (jniCheckHasExceptionOccurredAndClear())
 -         {
 -             // Failed to open file stream for resource
 -             jassertfalse;
 -             return {};
 -         }
 - 
 -         auto tempFile = File::createTempFile ({});
 -         tempFile.createDirectory();
 -         tempFile = tempFile.getChildFile (filename);
 - 
 -         StreamCloser outputStream  (LocalRef<jobject> (env->NewObject (JavaFileOutputStream,
 -                                                                        JavaFileOutputStream.constructor,
 -                                                                        javaString (tempFile.getFullPathName()).get())));
 - 
 -         if (jniCheckHasExceptionOccurredAndClear())
 -         {
 -             // Failed to open file stream for temporary file
 -             jassertfalse;
 -             return {};
 -         }
 - 
 -         LocalRef<jbyteArray> buffer (env->NewByteArray (1024));
 -         int bytesRead = 0;
 - 
 -         for (;;)
 -         {
 -             bytesRead = env->CallIntMethod (inputStream.stream, JavaFileInputStream.read, buffer.get());
 - 
 -             if (jniCheckHasExceptionOccurredAndClear())
 -             {
 -                 // Failed to read from resource file.
 -                 jassertfalse;
 -                 return {};
 -             }
 - 
 -             if (bytesRead < 0)
 -                 break;
 - 
 -             env->CallVoidMethod (outputStream.stream, JavaFileOutputStream.write, buffer.get(), 0, bytesRead);
 - 
 -             if (jniCheckHasExceptionOccurredAndClear())
 -             {
 -                 // Failed to write to temporary file.
 -                 jassertfalse;
 -                 return {};
 -             }
 -         }
 - 
 -         return URL (tempFile);
 -     }
 - 
 -     std::map<String, File> infoForUri;
 -     StringArray mimeTypes;
 -     std::function<void (const std::map<String, File>&, const StringArray&)> onCompletion;
 -     // This task is obtained from std::async(). Its destructor will block until the asynchronous
 -     // task has completed; as a result, we can guarantee that the async task will have finished
 -     // before the lifetimes of the other data members and base class end.
 -     std::future<void> task;
 - };
 - 
 - auto detail::ScopedContentSharerInterface::shareFiles (const Array<URL>& urls, Component*) -> std::unique_ptr<ScopedContentSharerInterface>
 - {
 -     class NativeScopedContentSharerInterface final : public detail::ScopedContentSharerInterface
 -     {
 -     public:
 -         explicit NativeScopedContentSharerInterface (Array<URL> f)
 -             : files (std::move (f)) {}
 - 
 -         void runAsync (ContentSharer::Callback callback) override
 -         {
 -             // This lambda will only be called if the AndroidContentSharerPrepareFilesTask is still
 -             // alive. We know that our lifetime will end after the
 -             // AndroidContentSharerPrepareFilesTask, so there's no need to check that 'this' is
 -             // still valid inside the lambda.
 -             task.emplace (files, [this, callback] (const std::map<String, File>& infoForUri, const StringArray& mimeTypes)
 -             {
 -                 launcher = ContentSharerGlobalImpl::getInstance().sharePreparedFiles (infoForUri, mimeTypes, [callback] (bool success)
 -                 {
 -                     callback (success, {});
 -                 });
 -             });
 -         }
 - 
 -         void close() override
 -         {
 -             // dismiss() doesn't close the sharesheet, and there doesn't seem to be any alternative
 -             // Maybe this will work in the future...
 -             launcher.reset();
 -         }
 - 
 -     private:
 -         Array<URL> files;
 -         std::optional<AndroidContentSharerPrepareFilesTask> task;
 -         std::unique_ptr<ActivityLauncher> launcher;
 -     };
 - 
 -     return std::make_unique<NativeScopedContentSharerInterface> (std::move (urls));
 - }
 - 
 - auto detail::ScopedContentSharerInterface::shareText (const String& text, Component*) -> std::unique_ptr<ScopedContentSharerInterface>
 - {
 -     class NativeScopedContentSharerInterface final : public detail::ScopedContentSharerInterface
 -     {
 -     public:
 -         explicit NativeScopedContentSharerInterface (String t)
 -             : text (std::move (t)) {}
 - 
 -         void runAsync (ContentSharer::Callback callback) override
 -         {
 -             launcher = ContentSharerGlobalImpl::getInstance().shareText (text, [callback] (bool success)
 -             {
 -                 callback (success, {});
 -             });
 -         }
 - 
 -         void close() override
 -         {
 -             // dismiss() doesn't close the sharesheet, and there doesn't seem to be any alternative
 -             // Maybe this will work in the future...
 -             launcher.reset();
 -         }
 - 
 -     private:
 -         String text;
 -         std::unique_ptr<ActivityLauncher> launcher;
 -     };
 - 
 -     return std::make_unique<NativeScopedContentSharerInterface> (std::move (text));
 - }
 - 
 - } // namespace juce
 
 
  |