2018-06-17 20:34:53 +08:00
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file is part of the JUCE library.
|
|
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
|
|
|
|
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 5 End-User License
|
|
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
|
|
27th April 2017).
|
|
|
|
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
|
|
Privacy Policy: www.juce.com/juce-5-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
|
|
|
|
{
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
//==============================================================================
|
|
|
|
// This byte-code is generated from native/javacore/app/com/roli/juce/JuceSharingContentProvider.java with min sdk version 16
|
|
|
|
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
|
|
|
static const uint8 javaJuceSharingContentProvider[] =
|
|
|
|
{31,139,8,8,143,106,229,91,0,3,74,117,99,101,83,104,97,114,105,110,103,67,111,110,116,101,110,116,80,114,111,118,105,100,
|
|
|
|
101,114,46,100,101,120,0,149,151,93,108,20,85,20,199,207,157,153,157,253,236,178,91,170,20,145,178,229,83,80,216,242,165,
|
|
|
|
96,5,11,45,72,183,91,139,161,52,218,190,56,221,157,148,129,221,153,101,102,118,133,23,2,106,162,209,196,24,125,64,19,73,
|
|
|
|
48,33,106,140,15,36,26,227,131,49,152,24,163,241,65,77,148,248,160,209,152,152,24,193,68,227,131,6,37,241,127,63,118,219,
|
|
|
|
173,197,232,194,111,238,185,231,156,123,238,185,231,222,153,206,148,237,19,137,190,173,219,105,239,208,216,231,67,47,106,
|
|
|
|
177,200,154,39,135,207,172,189,226,63,113,230,173,189,99,175,63,244,123,185,131,168,70,68,39,38,182,117,146,250,157,79,
|
|
|
|
17,141,146,212,223,4,46,48,34,110,252,3,109,4,237,103,26,209,82,222,71,171,163,189,132,203,80,156,40,103,16,125,111,18,
|
|
|
|
253,4,126,6,191,129,107,224,58,232,137,18,245,130,53,96,3,216,2,14,131,6,120,25,188,11,190,1,191,128,100,140,104,19,112,
|
|
|
|
192,235,224,50,184,6,110,193,28,187,192,3,192,6,117,240,52,120,6,60,15,206,130,115,224,101,240,10,120,3,188,9,222,6,159,
|
|
|
|
128,175,192,183,224,42,136,38,136,214,129,33,48,5,60,240,8,56,5,206,130,87,193,69,240,54,120,31,124,12,62,5,95,130,31,
|
|
|
|
192,21,240,43,248,19,24,73,162,197,96,57,88,5,242,224,78,176,27,12,131,7,65,9,56,224,56,56,9,78,129,199,192,83,0,101,37,
|
|
|
|
148,142,16,138,208,37,148,159,176,45,148,6,139,64,6,100,73,238,193,98,208,165,246,229,102,176,4,116,147,220,143,91,193,
|
|
|
|
106,176,134,228,190,240,223,195,168,189,166,228,10,228,152,154,235,4,100,148,65,236,231,105,165,71,233,233,89,200,248,47,
|
|
|
|
108,252,23,83,50,247,143,170,60,94,48,229,92,205,3,179,92,201,231,249,62,43,249,53,200,43,148,124,17,242,42,37,191,11,
|
|
|
|
121,165,146,63,130,220,171,228,47,32,231,148,252,181,41,215,177,120,78,14,93,42,135,4,170,181,85,212,42,69,247,137,122,
|
|
|
|
201,126,82,245,83,168,214,157,196,215,28,19,99,13,172,176,143,248,154,22,137,190,9,253,58,17,51,45,250,9,81,105,222,74,
|
|
|
|
125,2,255,214,171,120,36,218,36,109,16,109,156,238,17,241,101,220,20,42,113,187,104,53,186,67,180,58,109,20,45,163,77,
|
|
|
|
202,190,89,180,81,218,34,90,131,182,171,252,250,213,184,93,162,53,105,183,26,191,71,237,253,1,177,231,49,149,151,172,185,
|
|
|
|
169,106,193,247,171,15,157,109,50,61,113,94,178,170,70,77,251,0,236,35,202,158,82,118,109,142,253,32,236,211,202,206,245,
|
|
|
|
157,144,187,83,179,114,111,74,158,201,13,41,238,31,17,250,231,146,114,142,41,198,168,150,211,104,128,38,53,126,66,117,
|
|
|
|
120,242,179,118,46,41,207,137,151,209,225,127,8,91,89,235,139,146,198,210,34,119,83,248,92,104,197,208,97,53,104,32,50,
|
|
|
|
169,105,136,17,129,149,231,117,49,41,215,121,8,241,107,227,113,210,54,167,17,139,137,92,222,73,202,181,214,50,60,183,149,
|
|
|
|
168,79,45,195,207,253,84,198,16,59,25,17,167,154,232,189,164,90,7,246,155,199,229,249,125,152,148,117,24,239,53,104,57,
|
|
|
|
171,245,165,104,139,145,162,30,150,197,222,247,176,117,34,183,152,152,39,78,186,170,212,103,173,56,89,68,150,119,211,229,
|
|
|
|
57,58,77,100,133,103,86,83,151,153,157,239,251,121,243,117,252,203,124,166,26,115,37,41,239,233,241,45,24,163,241,49,131,
|
|
|
|
145,20,237,128,159,155,225,51,165,88,143,150,101,157,184,222,118,189,3,215,117,76,222,227,89,17,167,19,126,188,202,140,
|
|
|
|
174,183,205,221,48,121,5,111,60,119,68,172,33,158,154,173,89,206,160,182,223,29,243,250,59,230,245,121,55,138,168,89,220,
|
|
|
|
161,186,144,179,226,94,213,148,28,17,109,151,208,102,91,122,93,84,47,218,58,151,89,209,231,232,170,205,170,216,252,126,
|
|
|
|
202,42,61,151,155,177,179,202,175,139,204,123,28,215,9,119,19,27,38,99,184,88,44,82,132,95,139,196,10,180,162,80,47,217,
|
|
|
|
135,142,88,190,227,206,12,122,110,104,187,225,65,223,107,56,101,219,223,116,212,106,88,196,138,164,193,85,231,254,102,81,
|
|
|
|
252,168,183,104,185,101,223,115,202,249,146,28,146,159,55,180,159,86,220,200,101,194,170,212,237,160,159,214,255,195,193,
|
|
|
|
183,131,252,158,32,176,195,253,78,197,30,178,131,146,239,212,66,15,177,150,182,92,203,86,104,77,91,129,157,31,172,251,
|
|
|
|
129,215,54,77,203,52,106,133,190,115,162,233,144,109,57,184,118,152,63,236,59,115,195,121,65,158,207,53,54,29,216,126,
|
|
|
|
131,103,221,59,215,116,208,242,75,118,101,126,50,59,139,37,175,154,247,189,138,147,63,138,210,229,111,92,191,213,77,161,
|
|
|
|
153,203,189,255,127,104,123,122,27,254,115,128,126,90,89,44,91,149,134,115,44,111,185,174,23,90,161,227,185,249,125,110,
|
|
|
|
169,226,5,220,187,98,5,216,131,158,5,124,134,93,23,25,75,123,239,2,246,81,187,58,173,28,248,54,118,22,249,41,201,87,44,
|
|
|
|
119,38,63,54,125,212,46,133,237,186,67,33,207,174,159,210,237,197,160,174,133,86,72,108,130,244,137,97,156,184,137,2,25,
|
|
|
|
19,5,33,225,236,77,20,113,112,39,138,5,28,92,126,29,38,54,73,139,167,22,152,37,105,149,74,118,16,236,175,88,51,1,69,248,
|
|
|
|
98,109,74,150,188,74,189,234,222,111,85,237,128,150,170,195,198,171,214,204,101,144,187,149,169,167,205,52,55,173,125,13,
|
|
|
|
168,105,89,155,253,62,59,196,164,182,85,29,63,89,67,220,155,218,140,99,53,219,229,1,168,179,77,253,64,221,246,79,146,89,
|
|
|
|
182,43,118,104,83,196,22,97,151,204,216,225,66,39,141,210,51,237,83,68,209,231,18,25,71,188,32,164,56,191,142,123,135,
|
|
|
|
177,66,211,113,145,104,72,70,197,43,29,35,163,106,5,199,40,93,117,170,54,119,71,212,16,149,53,170,94,25,67,93,84,129,98,
|
|
|
|
158,59,136,184,200,33,234,185,114,113,29,30,82,110,221,124,240,104,174,192,168,89,225,17,74,212,124,143,239,45,14,0,69,
|
|
|
|
142,203,101,224,118,173,87,144,71,128,229,72,75,71,75,220,227,163,254,113,212,54,28,243,203,124,246,240,136,19,144,201,
|
|
|
|
175,171,251,200,172,215,202,124,118,189,238,59,252,82,161,72,131,63,21,200,20,77,64,155,244,3,219,215,71,211,27,119,109,
|
|
|
|
164,187,40,154,222,53,73,203,140,3,219,7,118,72,213,42,173,111,32,154,158,196,147,24,38,178,244,194,208,190,104,154,30,
|
|
|
|
99,90,97,39,20,14,205,176,2,250,227,90,97,20,205,16,156,168,170,21,238,22,166,134,20,138,58,254,116,108,156,26,193,147,
|
|
|
|
119,36,50,178,103,104,223,126,97,157,50,10,163,34,150,214,193,70,186,83,90,90,91,107,100,239,94,114,75,83,88,166,45,98,
|
|
|
|
35,183,106,221,137,238,36,105,26,195,159,238,103,115,145,211,167,141,75,49,237,81,141,76,246,93,140,171,53,174,142,157,
|
|
|
|
57,109,60,30,103,80,39,216,133,56,49,35,110,104,73,232,46,9,93,147,69,236,199,56,99,127,129,139,9,198,62,0,95,129,171,
|
|
|
|
224,124,146,177,31,193,75,41,249,110,75,234,89,222,108,155,223,30,252,57,223,252,254,208,105,246,27,196,160,217,239,16,
|
|
|
|
222,54,191,69,76,154,253,30,209,51,82,230,127,207,88,78,190,75,15,64,54,115,82,207,223,161,88,70,190,103,139,119,228,156,
|
|
|
|
156,151,127,191,232,202,159,191,243,24,57,57,31,127,47,34,53,86,188,123,101,100,174,252,91,233,111,138,244,241,33,100,13,
|
|
|
|
0,0};
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
//==============================================================================
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
2018-06-17 20:34:53 +08:00
|
|
|
FIELD (authority, "authority", "Ljava/lang/String;")
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo")
|
2018-06-17 20:34:53 +08:00
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
2018-06-17 20:34:53 +08:00
|
|
|
METHOD (constructor, "<init>", "(Landroid/os/ParcelFileDescriptor;JJ)V") \
|
|
|
|
METHOD (createInputStream, "createInputStream", "()Ljava/io/FileInputStream;") \
|
|
|
|
METHOD (getLength, "getLength", "()J")
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
DECLARE_JNI_CLASS (AssetFileDescriptor, "android/content/res/AssetFileDescriptor")
|
2018-06-17 20:34:53 +08:00
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
2018-06-17 20:34:53 +08:00
|
|
|
METHOD (close, "close", "()V")
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
DECLARE_JNI_CLASS (JavaCloseable, "java/io/Closeable")
|
2018-06-17 20:34:53 +08:00
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
2018-06-17 20:34:53 +08:00
|
|
|
STATICMETHOD (open, "open", "(Ljava/io/File;I)Landroid/os/ParcelFileDescriptor;")
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
DECLARE_JNI_CLASS (ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
|
2018-06-17 20:34:53 +08:00
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class AndroidContentSharerCursor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
class Owner
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~Owner() {}
|
|
|
|
|
|
|
|
virtual void cursorClosed (const AndroidContentSharerCursor&) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
AndroidContentSharerCursor (Owner& ownerToUse, JNIEnv* env,
|
|
|
|
const LocalRef<jobject>& contentProvider,
|
|
|
|
const LocalRef<jobjectArray>& resultColumns)
|
|
|
|
: owner (ownerToUse),
|
2019-06-23 03:41:38 +08:00
|
|
|
cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderCursor,
|
|
|
|
JuceContentProviderCursor.constructor,
|
2018-06-17 20:34:53 +08:00
|
|
|
contentProvider.get(),
|
|
|
|
reinterpret_cast<jlong> (this),
|
|
|
|
resultColumns.get()))))
|
|
|
|
{
|
|
|
|
// the content provider must be created first
|
|
|
|
jassert (contentProvider.get() != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
jobject getNativeCursor() { return cursor.get(); }
|
|
|
|
|
|
|
|
void cursorClosed()
|
|
|
|
{
|
|
|
|
MessageManager::callAsync ([this] { owner.cursorClosed (*this); });
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
void addRow (LocalRef<jobjectArray>& values)
|
|
|
|
{
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
env->CallVoidMethod (cursor.get(), JuceContentProviderCursor.addRow, values.get());
|
|
|
|
}
|
|
|
|
|
2018-06-17 20:34:53 +08:00
|
|
|
private:
|
|
|
|
Owner& owner;
|
|
|
|
GlobalRef cursor;
|
2019-06-23 03:41:38 +08:00
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
|
|
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
|
|
|
|
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;J[Ljava/lang/String;)V") \
|
|
|
|
CALLBACK (contentSharerCursorClosed, "contentSharerCursorClosed", "(J)V") \
|
|
|
|
|
|
|
|
DECLARE_JNI_CLASS (JuceContentProviderCursor, "com/roli/juce/JuceSharingContentProvider$ProviderCursor")
|
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
|
|
|
static void JNICALL contentSharerCursorClosed(JNIEnv*, jobject, jlong host)
|
|
|
|
{
|
|
|
|
if (auto* myself = reinterpret_cast<AndroidContentSharerCursor*> (host))
|
|
|
|
myself->cursorClosed();
|
|
|
|
}
|
2018-06-17 20:34:53 +08:00
|
|
|
};
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
AndroidContentSharerCursor::JuceContentProviderCursor_Class AndroidContentSharerCursor::JuceContentProviderCursor;
|
|
|
|
|
2018-06-17 20:34:53 +08:00
|
|
|
//==============================================================================
|
|
|
|
class AndroidContentSharerFileObserver
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
class Owner
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual ~Owner() {}
|
|
|
|
|
|
|
|
virtual void fileHandleClosed (const AndroidContentSharerFileObserver&) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
AndroidContentSharerFileObserver (Owner& ownerToUse, JNIEnv* env,
|
|
|
|
const LocalRef<jobject>& contentProvider,
|
|
|
|
const String& filepathToUse)
|
|
|
|
: owner (ownerToUse),
|
|
|
|
filepath (filepathToUse),
|
|
|
|
fileObserver (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderFileObserver,
|
|
|
|
JuceContentProviderFileObserver.constructor,
|
|
|
|
contentProvider.get(),
|
|
|
|
reinterpret_cast<jlong> (this),
|
|
|
|
javaString (filepath).get(),
|
|
|
|
open | access | closeWrite | closeNoWrite))))
|
|
|
|
{
|
|
|
|
// the content provider must be created first
|
|
|
|
jassert (contentProvider.get() != 0);
|
|
|
|
|
|
|
|
env->CallVoidMethod (fileObserver, JuceContentProviderFileObserver.startWatching);
|
|
|
|
}
|
|
|
|
|
|
|
|
void onFileEvent (int event, const LocalRef<jstring>& path)
|
|
|
|
{
|
|
|
|
ignoreUnused (path);
|
|
|
|
|
|
|
|
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 ([this]
|
|
|
|
{
|
|
|
|
getEnv()->CallVoidMethod (fileObserver, JuceContentProviderFileObserver.stopWatching);
|
|
|
|
owner.fileHandleClosed (*this);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr int open = 32;
|
|
|
|
static constexpr int access = 1;
|
|
|
|
static constexpr int closeWrite = 8;
|
|
|
|
static constexpr int closeNoWrite = 16;
|
|
|
|
|
|
|
|
bool fileWasRead = false;
|
|
|
|
int numOpenedHandles = 0;
|
|
|
|
|
|
|
|
Owner& owner;
|
|
|
|
String filepath;
|
|
|
|
GlobalRef fileObserver;
|
2019-06-23 03:41:38 +08:00
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
|
|
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;JLjava/lang/String;I)V") \
|
|
|
|
METHOD (startWatching, "startWatching", "()V") \
|
|
|
|
METHOD (stopWatching, "stopWatching", "()V") \
|
|
|
|
CALLBACK (contentSharerFileObserverEvent, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \
|
|
|
|
|
|
|
|
DECLARE_JNI_CLASS (JuceContentProviderFileObserver, "com/roli/juce/JuceSharingContentProvider$ProviderFileObserver")
|
|
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
|
|
|
|
static void JNICALL contentSharerFileObserverEvent (JNIEnv*, jobject /*fileObserver*/, jlong host, int event, jstring path)
|
|
|
|
{
|
|
|
|
if (auto* myself = reinterpret_cast<AndroidContentSharerFileObserver*> (host))
|
|
|
|
myself->onFileEvent (event, LocalRef<jstring> (path));
|
|
|
|
}
|
2018-06-17 20:34:53 +08:00
|
|
|
};
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
AndroidContentSharerFileObserver::JuceContentProviderFileObserver_Class AndroidContentSharerFileObserver::JuceContentProviderFileObserver;
|
|
|
|
|
2018-06-17 20:34:53 +08:00
|
|
|
//==============================================================================
|
|
|
|
class AndroidContentSharerPrepareFilesThread : private Thread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AndroidContentSharerPrepareFilesThread (AsyncUpdater& ownerToUse,
|
|
|
|
const Array<URL>& fileUrlsToUse,
|
|
|
|
const String& packageNameToUse,
|
|
|
|
const String& uriBaseToUse)
|
|
|
|
: Thread ("AndroidContentSharerPrepareFilesThread"),
|
|
|
|
owner (ownerToUse),
|
|
|
|
fileUrls (fileUrlsToUse),
|
|
|
|
resultFileUris (GlobalRef (LocalRef<jobject> (getEnv()->NewObject (JavaArrayList,
|
|
|
|
JavaArrayList.constructor,
|
|
|
|
fileUrls.size())))),
|
|
|
|
packageName (packageNameToUse),
|
|
|
|
uriBase (uriBaseToUse)
|
|
|
|
{
|
|
|
|
startThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
~AndroidContentSharerPrepareFilesThread()
|
|
|
|
{
|
|
|
|
signalThreadShouldExit();
|
|
|
|
waitForThreadToExit (10000);
|
|
|
|
|
|
|
|
for (auto& f : temporaryFilesFromAssetFiles)
|
|
|
|
f.deleteFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
jobject getResultFileUris() { return resultFileUris.get(); }
|
|
|
|
const StringArray& getMimeTypes() const { return mimeTypes; }
|
|
|
|
const StringArray& getFilePaths() const { return filePaths; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct StreamCloser
|
|
|
|
{
|
2019-06-23 03:41:38 +08:00
|
|
|
StreamCloser (const LocalRef<jobject>& streamToUse)
|
2018-06-17 20:34:53 +08:00
|
|
|
: stream (GlobalRef (streamToUse))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~StreamCloser()
|
|
|
|
{
|
|
|
|
if (stream.get() != 0)
|
|
|
|
getEnv()->CallVoidMethod (stream, JavaCloseable.close);
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobalRef stream;
|
|
|
|
};
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
bool canSpecifyMimeTypes = true;
|
|
|
|
|
|
|
|
for (auto f : fileUrls)
|
|
|
|
{
|
|
|
|
auto scheme = f.getScheme();
|
|
|
|
|
|
|
|
// Only "file://" scheme or no scheme (for files in app bundle) are allowed!
|
|
|
|
jassert (scheme.isEmpty() || scheme == "file");
|
|
|
|
|
|
|
|
if (scheme.isEmpty())
|
|
|
|
{
|
|
|
|
// 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
|
|
|
|
f = copyAssetFileToTemporaryFile (env, f.toString (true));
|
|
|
|
|
|
|
|
if (f.isEmpty())
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threadShouldExit())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto filepath = URL::removeEscapeChars (f.toString (true).fromFirstOccurrenceOf ("file://", false, false));
|
|
|
|
|
|
|
|
filePaths.add (filepath);
|
|
|
|
|
|
|
|
auto filename = filepath.fromLastOccurrenceOf ("/", false, true);
|
|
|
|
auto fileExtension = filename.fromLastOccurrenceOf (".", false, true);
|
|
|
|
auto contentString = uriBase + String (filePaths.size() - 1) + "/" + filename;
|
|
|
|
|
|
|
|
auto uri = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
|
|
|
|
javaString (contentString).get()));
|
|
|
|
|
|
|
|
if (canSpecifyMimeTypes)
|
|
|
|
canSpecifyMimeTypes = fileExtension.isNotEmpty();
|
|
|
|
|
|
|
|
if (canSpecifyMimeTypes)
|
|
|
|
mimeTypes.addArray (getMimeTypesForFileExtension (fileExtension));
|
|
|
|
else
|
|
|
|
mimeTypes.clear();
|
|
|
|
|
|
|
|
env->CallBooleanMethod (resultFileUris, JavaArrayList.add, uri.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
owner.triggerAsyncUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
URL copyAssetFileToTemporaryFile (JNIEnv* env, const String& filename)
|
|
|
|
{
|
2019-06-23 03:41:38 +08:00
|
|
|
auto resources = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources));
|
2018-06-17 20:34:53 +08:00
|
|
|
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
|
2019-06-23 03:41:38 +08:00
|
|
|
// and that you specify just the file name, without an extension.
|
2018-06-17 20:34:53 +08:00
|
|
|
jassert (fileId != 0);
|
|
|
|
|
|
|
|
if (fileId == 0)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
auto assetFd = LocalRef<jobject> (env->CallObjectMethod (resources,
|
|
|
|
AndroidResources.openRawResourceFd,
|
|
|
|
fileId));
|
|
|
|
|
|
|
|
auto inputStream = StreamCloser (LocalRef<jobject> (env->CallObjectMethod (assetFd,
|
|
|
|
AssetFileDescriptor.createInputStream)));
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
if (jniCheckHasExceptionOccurredAndClear())
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
// Failed to open file stream for resource
|
|
|
|
jassertfalse;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto tempFile = File::createTempFile ({});
|
|
|
|
tempFile.createDirectory();
|
|
|
|
tempFile = tempFile.getChildFile (filename);
|
|
|
|
|
|
|
|
auto outputStream = StreamCloser (LocalRef<jobject> (env->NewObject (JavaFileOutputStream,
|
|
|
|
JavaFileOutputStream.constructor,
|
|
|
|
javaString (tempFile.getFullPathName()).get())));
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
if (jniCheckHasExceptionOccurredAndClear())
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
// Failed to open file stream for temporary file
|
|
|
|
jassertfalse;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buffer = LocalRef<jbyteArray> (env->NewByteArray (1024));
|
|
|
|
int bytesRead = 0;
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
for (;;)
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
if (threadShouldExit())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
bytesRead = env->CallIntMethod (inputStream.stream, JavaFileInputStream.read, buffer.get());
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
if (jniCheckHasExceptionOccurredAndClear())
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
// Failed to read from resource file.
|
|
|
|
jassertfalse;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytesRead < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
env->CallVoidMethod (outputStream.stream, JavaFileOutputStream.write, buffer.get(), 0, bytesRead);
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
if (jniCheckHasExceptionOccurredAndClear())
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
// Failed to write to temporary file.
|
|
|
|
jassertfalse;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
temporaryFilesFromAssetFiles.add (tempFile);
|
|
|
|
|
|
|
|
return URL (tempFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncUpdater& owner;
|
|
|
|
Array<URL> fileUrls;
|
|
|
|
|
|
|
|
GlobalRef resultFileUris;
|
|
|
|
String packageName;
|
|
|
|
String uriBase;
|
|
|
|
|
|
|
|
StringArray filePaths;
|
|
|
|
Array<File> temporaryFilesFromAssetFiles;
|
|
|
|
StringArray mimeTypes;
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl,
|
|
|
|
public AndroidContentSharerFileObserver::Owner,
|
|
|
|
public AndroidContentSharerCursor::Owner,
|
|
|
|
public AsyncUpdater,
|
|
|
|
private Timer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ContentSharerNativeImpl (ContentSharer& cs)
|
|
|
|
: owner (cs),
|
2019-06-23 03:41:38 +08:00
|
|
|
packageName (juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName)))),
|
2018-06-17 20:34:53 +08:00
|
|
|
uriBase ("content://" + packageName + ".sharingcontentprovider/")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~ContentSharerNativeImpl()
|
|
|
|
{
|
|
|
|
masterReference.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void shareFiles (const Array<URL>& files) override
|
|
|
|
{
|
|
|
|
if (! isContentSharingEnabled())
|
|
|
|
{
|
|
|
|
// You need to enable "Content Sharing" in Projucer's Android exporter.
|
|
|
|
jassertfalse;
|
|
|
|
owner.sharingFinished (false, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareFilesThread.reset (new AndroidContentSharerPrepareFilesThread (*this, files, packageName, uriBase));
|
|
|
|
}
|
|
|
|
|
|
|
|
void shareText (const String& text) override
|
|
|
|
{
|
|
|
|
if (! isContentSharingEnabled())
|
|
|
|
{
|
|
|
|
// You need to enable "Content Sharing" in Projucer's Android exporter.
|
|
|
|
jassertfalse;
|
|
|
|
owner.sharingFinished (false, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
auto intent = LocalRef<jobject> (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());
|
|
|
|
|
|
|
|
auto chooserIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent, AndroidIntent.createChooser,
|
|
|
|
intent.get(), javaString ("Choose share target").get()));
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
WeakReference<ContentSharerNativeImpl> weakRef (this);
|
|
|
|
|
|
|
|
startAndroidActivityForResult (chooserIntent, 1003,
|
|
|
|
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
|
|
|
|
{
|
|
|
|
if (weakRef != nullptr)
|
|
|
|
weakRef->sharingFinished (resultCode);
|
|
|
|
});
|
2018-06-17 20:34:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void cursorClosed (const AndroidContentSharerCursor& cursor) override
|
|
|
|
{
|
|
|
|
cursors.removeObject (&cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fileHandleClosed (const AndroidContentSharerFileObserver&) override
|
|
|
|
{
|
|
|
|
decrementPendingFileCountAndNotifyOwnerIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
2019-06-23 03:41:38 +08:00
|
|
|
jobject openFile (const LocalRef<jobject>& contentProvider,
|
2018-06-17 20:34:53 +08:00
|
|
|
const LocalRef<jobject>& uri, const LocalRef<jstring>& mode)
|
|
|
|
{
|
|
|
|
ignoreUnused (mode);
|
|
|
|
|
|
|
|
WeakReference<ContentSharerNativeImpl> weakRef (this);
|
|
|
|
|
|
|
|
if (weakRef == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
auto uriElements = getContentUriElements (env, uri);
|
|
|
|
|
|
|
|
if (uriElements.filepath.isEmpty())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return getAssetFileDescriptor (env, contentProvider, uriElements.filepath);
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
jobject query (const LocalRef<jobject>& contentProvider, const LocalRef<jobject>& uri,
|
2018-06-17 20:34:53 +08:00
|
|
|
const LocalRef<jobjectArray>& projection, const LocalRef<jobject>& selection,
|
|
|
|
const LocalRef<jobjectArray>& selectionArgs, const LocalRef<jobject>& sortOrder)
|
|
|
|
{
|
|
|
|
ignoreUnused (selection, selectionArgs, sortOrder);
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
auto cursor = cursors.add (new AndroidContentSharerCursor (*this, env, contentProvider,
|
|
|
|
resultJavaColumns));
|
|
|
|
|
|
|
|
auto uriElements = getContentUriElements (env, uri);
|
|
|
|
|
|
|
|
if (uriElements.filepath.isEmpty())
|
|
|
|
return cursor->getNativeCursor();
|
|
|
|
|
|
|
|
auto values = LocalRef<jobjectArray> (env->NewObjectArray ((jsize) resultColumns.size(),
|
|
|
|
JavaObject, 0));
|
|
|
|
|
|
|
|
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")
|
|
|
|
{
|
|
|
|
auto javaFile = LocalRef<jobject> (env->NewObject (JavaFile, JavaFile.constructor,
|
|
|
|
javaString (uriElements.filepath).get()));
|
|
|
|
|
|
|
|
jlong fileLength = env->CallLongMethod (javaFile, JavaFile.length);
|
|
|
|
|
|
|
|
env->SetObjectArrayElement (values, i, env->NewObject (JavaLong,
|
|
|
|
JavaLong.constructor,
|
|
|
|
fileLength));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
cursor->addRow (values);
|
|
|
|
return cursor->getNativeCursor();
|
2018-06-17 20:34:53 +08:00
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
jobjectArray getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
auto extension = getContentUriElements (env, uri).filename.fromLastOccurrenceOf (".", false, true);
|
|
|
|
|
|
|
|
if (extension.isEmpty())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return juceStringArrayToJava (filterMimeTypes (getMimeTypesForFileExtension (extension),
|
|
|
|
juceString (mimeTypeFilter.get())));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sharingFinished (int resultCode)
|
|
|
|
{
|
|
|
|
sharingActivityDidFinish = true;
|
|
|
|
|
|
|
|
succeeded = resultCode == -1;
|
|
|
|
|
|
|
|
// Give content sharer a chance to request file access.
|
|
|
|
if (nonAssetFilesPendingShare.get() == 0)
|
|
|
|
startTimer (2000);
|
|
|
|
else
|
|
|
|
notifyOwnerIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool isContentSharingEnabled() const
|
|
|
|
{
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
LocalRef<jobject> packageManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
|
2018-06-17 20:34:53 +08:00
|
|
|
|
|
|
|
constexpr int getProviders = 8;
|
|
|
|
auto packageInfo = LocalRef<jobject> (env->CallObjectMethod (packageManager,
|
|
|
|
AndroidPackageManager.getPackageInfo,
|
|
|
|
javaString (packageName).get(),
|
|
|
|
getProviders));
|
|
|
|
auto providers = LocalRef<jobjectArray> ((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)
|
|
|
|
{
|
|
|
|
auto providerInfo = LocalRef<jobject> (env->GetObjectArrayElement (providers, i));
|
|
|
|
auto authority = LocalRef<jstring> ((jstring) env->GetObjectField (providerInfo,
|
|
|
|
AndroidProviderInfo.authority));
|
|
|
|
|
|
|
|
if (juceString (authority) == sharingContentProviderAuthority)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleAsyncUpdate() override
|
|
|
|
{
|
|
|
|
jassert (prepareFilesThread != nullptr);
|
|
|
|
|
|
|
|
if (prepareFilesThread == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
filesPrepared (prepareFilesThread->getResultFileUris(), prepareFilesThread->getMimeTypes());
|
|
|
|
}
|
|
|
|
|
|
|
|
void filesPrepared (jobject fileUris, const StringArray& mimeTypes)
|
|
|
|
{
|
|
|
|
auto* env = getEnv();
|
|
|
|
|
|
|
|
auto intent = LocalRef<jobject> (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);
|
|
|
|
|
|
|
|
auto chooserIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent,
|
|
|
|
AndroidIntent.createChooser,
|
|
|
|
intent.get(),
|
|
|
|
javaString ("Choose share target").get()));
|
2019-06-23 03:41:38 +08:00
|
|
|
WeakReference<ContentSharerNativeImpl> weakRef (this);
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
startAndroidActivityForResult (chooserIntent, 1003,
|
|
|
|
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
|
|
|
|
{
|
|
|
|
if (weakRef != nullptr)
|
|
|
|
weakRef->sharingFinished (resultCode);
|
|
|
|
});
|
2018-06-17 20:34:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void decrementPendingFileCountAndNotifyOwnerIfReady()
|
|
|
|
{
|
|
|
|
--nonAssetFilesPendingShare;
|
|
|
|
|
|
|
|
notifyOwnerIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
void notifyOwnerIfReady()
|
|
|
|
{
|
|
|
|
if (sharingActivityDidFinish && nonAssetFilesPendingShare.get() == 0)
|
|
|
|
owner.sharingFinished (succeeded, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
void timerCallback() override
|
|
|
|
{
|
|
|
|
stopTimer();
|
|
|
|
|
|
|
|
notifyOwnerIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
struct ContentUriElements
|
|
|
|
{
|
|
|
|
String index;
|
|
|
|
String filename;
|
|
|
|
String filepath;
|
|
|
|
};
|
|
|
|
|
|
|
|
ContentUriElements getContentUriElements (JNIEnv* env, const LocalRef<jobject>& uri) const
|
|
|
|
{
|
|
|
|
jassert (prepareFilesThread != nullptr);
|
|
|
|
|
|
|
|
if (prepareFilesThread == nullptr)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
auto fullUri = juceString ((jstring) env->CallObjectMethod (uri.get(), AndroidUri.toString));
|
|
|
|
|
|
|
|
auto index = fullUri.fromFirstOccurrenceOf (uriBase, false, false)
|
|
|
|
.upToFirstOccurrenceOf ("/", false, true);
|
|
|
|
|
|
|
|
auto filename = fullUri.fromLastOccurrenceOf ("/", false, true);
|
|
|
|
|
|
|
|
return { index, filename, prepareFilesThread->getFilePaths()[index.getIntValue()] };
|
|
|
|
}
|
|
|
|
|
|
|
|
static StringArray getSupportedColumns()
|
|
|
|
{
|
|
|
|
return StringArray ("_display_name", "_size");
|
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
jobject getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider,
|
2018-06-17 20:34:53 +08:00
|
|
|
const String& filepath)
|
|
|
|
{
|
|
|
|
// This function can be called from multiple threads.
|
|
|
|
{
|
|
|
|
const ScopedLock sl (nonAssetFileOpenLock);
|
|
|
|
|
|
|
|
if (! nonAssetFilePathsPendingShare.contains (filepath))
|
|
|
|
{
|
|
|
|
nonAssetFilePathsPendingShare.add (filepath);
|
|
|
|
++nonAssetFilesPendingShare;
|
|
|
|
|
|
|
|
nonAssetFileObservers.add (new AndroidContentSharerFileObserver (*this, env,
|
|
|
|
contentProvider,
|
|
|
|
filepath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto javaFile = LocalRef<jobject> (env->NewObject (JavaFile, JavaFile.constructor,
|
|
|
|
javaString (filepath).get()));
|
|
|
|
|
|
|
|
constexpr int modeReadOnly = 268435456;
|
|
|
|
auto parcelFileDescriptor = LocalRef<jobject> (env->CallStaticObjectMethod (ParcelFileDescriptor,
|
|
|
|
ParcelFileDescriptor.open,
|
|
|
|
javaFile.get(), modeReadOnly));
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
if (jniCheckHasExceptionOccurredAndClear())
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
ContentSharer& owner;
|
|
|
|
String packageName;
|
|
|
|
String uriBase;
|
|
|
|
|
|
|
|
std::unique_ptr<AndroidContentSharerPrepareFilesThread> prepareFilesThread;
|
|
|
|
|
|
|
|
bool succeeded = false;
|
|
|
|
String errorDescription;
|
|
|
|
|
|
|
|
bool sharingActivityDidFinish = false;
|
|
|
|
|
|
|
|
OwnedArray<AndroidContentSharerCursor> cursors;
|
|
|
|
|
|
|
|
Array<GlobalRef> assetFileDescriptors;
|
|
|
|
|
|
|
|
CriticalSection nonAssetFileOpenLock;
|
|
|
|
StringArray nonAssetFilePathsPendingShare;
|
|
|
|
Atomic<int> nonAssetFilesPendingShare { 0 };
|
|
|
|
OwnedArray<AndroidContentSharerFileObserver> nonAssetFileObservers;
|
|
|
|
|
|
|
|
WeakReference<ContentSharerNativeImpl>::Master masterReference;
|
|
|
|
friend class WeakReference<ContentSharerNativeImpl>;
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
//==============================================================================
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
|
|
CALLBACK (contentSharerQuery, "contentSharerQuery", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
|
|
|
|
CALLBACK (contentSharerOpenFile, "contentSharerOpenFile", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;") \
|
|
|
|
CALLBACK (contentSharerGetStreamTypes, "contentSharerGetStreamTypes", "(Landroid/net/Uri;Ljava/lang/String;)[Ljava/lang/String;") \
|
2018-06-17 20:34:53 +08:00
|
|
|
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceSharingContentProvider, "com/roli/juce/JuceSharingContentProvider", 16, javaJuceSharingContentProvider, sizeof(javaJuceSharingContentProvider))
|
|
|
|
#undef JNI_CLASS_MEMBERS
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
static jobject JNICALL contentSharerQuery (JNIEnv*, jobject contentProvider, jobject uri, jobjectArray projection,
|
|
|
|
jobject selection, jobjectArray selectionArgs, jobject sortOrder)
|
|
|
|
{
|
|
|
|
if (auto *pimpl = (ContentSharer::ContentSharerNativeImpl *) ContentSharer::getInstance ()->pimpl.get ())
|
|
|
|
return pimpl->query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
|
|
|
LocalRef<jobject> (static_cast<jobject> (uri)),
|
|
|
|
LocalRef<jobjectArray> (
|
|
|
|
static_cast<jobjectArray> (projection)),
|
|
|
|
LocalRef<jobject> (static_cast<jobject> (selection)),
|
|
|
|
LocalRef<jobjectArray> (
|
|
|
|
static_cast<jobjectArray> (selectionArgs)),
|
|
|
|
LocalRef<jobject> (static_cast<jobject> (sortOrder)));
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
static jobject JNICALL contentSharerOpenFile (JNIEnv*, jobject contentProvider, jobject uri, jstring mode)
|
|
|
|
{
|
|
|
|
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
|
|
|
|
return pimpl->openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
|
|
|
LocalRef<jobject> (static_cast<jobject> (uri)),
|
|
|
|
LocalRef<jstring> (static_cast<jstring> (mode)));
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
static jobjectArray JNICALL contentSharerGetStreamTypes (JNIEnv*, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter)
|
|
|
|
{
|
|
|
|
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
|
|
|
|
return pimpl->getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
|
|
|
|
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
};
|
2018-06-17 20:34:53 +08:00
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
//==============================================================================
|
|
|
|
ContentSharer::Pimpl* ContentSharer::createPimpl()
|
2018-06-17 20:34:53 +08:00
|
|
|
{
|
2019-06-23 03:41:38 +08:00
|
|
|
return new ContentSharerNativeImpl (*this);
|
2018-06-17 20:34:53 +08:00
|
|
|
}
|
|
|
|
|
2019-06-23 03:41:38 +08:00
|
|
|
ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider;
|
2018-06-17 20:34:53 +08:00
|
|
|
|
|
|
|
} // namespace juce
|