Browse Source

AndroidDocument: Make input stream more robust

Previously, input streams created by AndroidDocument instances did not
implement setPosition, so they were not useful for reading some file
formats, such as WAV.

Due to limitations of the Java InputStream interface, seeking backwards
in a stream requires creating a whole new stream and seeking from the
beginning, so it could be quite slow.
v7.0.9
reuk 3 years ago
parent
commit
0238561156
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
3 changed files with 82 additions and 45 deletions
  1. +7
    -12
      modules/juce_core/native/juce_android_AndroidDocument.cpp
  2. +75
    -14
      modules/juce_core/native/juce_android_Files.cpp
  3. +0
    -19
      modules/juce_core/native/juce_android_Network.cpp

+ 7
- 12
modules/juce_core/native/juce_android_AndroidDocument.cpp View File

@@ -417,14 +417,18 @@ struct AndroidDocument::Utils
return false;
}
std::unique_ptr<InputStream> createInputStream() const override
std::unique_ptr<InputStream> createInputStream() const override
{
return makeStream<AndroidContentUriInputStream> (AndroidStreamHelpers::StreamKind::input);
auto result = std::make_unique<AndroidContentUriInputStream> (uri);
return result->openedSuccessfully() ? std::move (result) : nullptr;
}
std::unique_ptr<OutputStream> createOutputStream() const override
{
return makeStream<AndroidContentUriOutputStream> (AndroidStreamHelpers::StreamKind::output);
auto stream = AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::output);
return stream.get() != nullptr ? std::make_unique<AndroidContentUriOutputStream> (std::move (stream))
: nullptr;
}
AndroidDocumentInfo getInfo() const override
@@ -507,15 +511,6 @@ struct AndroidDocument::Utils
NativeInfo getNativeInfo() const override { return { uri }; }
private:
template <typename Stream>
std::unique_ptr<Stream> makeStream (AndroidStreamHelpers::StreamKind kind) const
{
auto stream = AndroidStreamHelpers::createStream (uri, kind);
return stream.get() != nullptr ? std::make_unique<Stream> (std::move (stream))
: nullptr;
}
GlobalRef uri;
};


+ 75
- 14
modules/juce_core/native/juce_android_Files.cpp View File

@@ -79,7 +79,8 @@ DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([B)I")
METHOD (read, "read", "([B)I") \
METHOD (skip, "skip", "(J)J")
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream")
#undef JNI_CLASS_MEMBERS
@@ -550,15 +551,38 @@ private:
jsize size = 0;
};
//==============================================================================
struct AndroidStreamHelpers
{
enum class StreamKind { output, input };
static LocalRef<jobject> createStream (const GlobalRef& uri, StreamKind kind)
{
auto* env = getEnv();
auto contentResolver = AndroidContentUriResolver::getContentResolver();
if (contentResolver == nullptr)
return {};
return LocalRef<jobject> (env->CallObjectMethod (contentResolver.get(),
kind == StreamKind::input ? ContentResolver.openInputStream
: ContentResolver.openOutputStream,
uri.get()));
}
};
//==============================================================================
struct AndroidContentUriInputStream : public InputStream
{
explicit AndroidContentUriInputStream (LocalRef<jobject>&& streamIn)
: stream (std::move (streamIn)) {}
explicit AndroidContentUriInputStream (const GlobalRef& uriIn)
: uri (uriIn),
stream (AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::input))
{}
~AndroidContentUriInputStream() override
{
getEnv()->CallVoidMethod (stream.get(), AndroidInputStream.close);
jniCheckHasExceptionOccurredAndClear();
}
int64 getTotalLength() override { return -1; }
@@ -569,30 +593,36 @@ struct AndroidContentUriInputStream : public InputStream
{
auto* env = getEnv();
if ((jsize) maxBytesToRead > byteArray.getSize())
if ((jsize) maxBytesToRead != byteArray.getSize())
byteArray = CachedByteArray { (jsize) maxBytesToRead };
const auto result = env->CallIntMethod (stream.get(), AndroidInputStream.read, byteArray.getNativeArray());
if (result != -1)
{
pos += result;
auto* rawBytes = env->GetByteArrayElements (byteArray.getNativeArray(), nullptr);
std::memcpy (destBuffer, rawBytes, static_cast<size_t> (result));
env->ReleaseByteArrayElements (byteArray.getNativeArray(), rawBytes, 0);
}
else
if (jniCheckHasExceptionOccurredAndClear() || result == -1)
{
exhausted = true;
return -1;
}
pos += result;
auto* rawBytes = env->GetByteArrayElements (byteArray.getNativeArray(), nullptr);
std::memcpy (destBuffer, rawBytes, static_cast<size_t> (result));
env->ReleaseByteArrayElements (byteArray.getNativeArray(), rawBytes, 0);
return result;
}
bool setPosition (int64 newPos) override
{
return (newPos == pos);
if (newPos == pos)
return true;
if (pos < newPos)
return skipImpl (newPos - pos);
AndroidContentUriInputStream (uri).swap (*this);
return skipImpl (newPos);
}
int64 getPosition() override
@@ -600,6 +630,37 @@ struct AndroidContentUriInputStream : public InputStream
return pos;
}
bool openedSuccessfully() const { return stream != nullptr; }
void skipNextBytes (int64 num) override
{
skipImpl (num);
}
private:
bool skipImpl (int64 num)
{
if (stream == nullptr)
return false;
const auto skipped = getEnv()->CallLongMethod (stream, AndroidInputStream.skip, (jlong) num);
if (jniCheckHasExceptionOccurredAndClear())
return false;
pos += skipped;
return skipped == num;
}
auto tie() { return std::tie (uri, byteArray, stream, pos, exhausted); }
void swap (AndroidContentUriInputStream& other) noexcept
{
auto toSwap = other.tie();
tie().swap (toSwap);
}
GlobalRef uri;
CachedByteArray byteArray;
GlobalRef stream;
int64 pos = 0;


+ 0
- 19
modules/juce_core/native/juce_android_Network.cpp View File

@@ -312,25 +312,6 @@ String URL::getFileName() const
return toString (false).fromLastOccurrenceOf ("/", false, true);
}
struct AndroidStreamHelpers
{
enum class StreamKind { output, input };
static LocalRef<jobject> createStream (const GlobalRef& uri, StreamKind kind)
{
auto* env = getEnv();
auto contentResolver = AndroidContentUriResolver::getContentResolver();
if (contentResolver == nullptr)
return {};
return LocalRef<jobject> (env->CallObjectMethod (contentResolver.get(),
kind == StreamKind::input ? ContentResolver.openInputStream
: ContentResolver.openOutputStream,
uri.get()));
}
};
//==============================================================================
class WebInputStream::Pimpl
{


Loading…
Cancel
Save