diff --git a/modules/juce_core/native/juce_android_AndroidDocument.cpp b/modules/juce_core/native/juce_android_AndroidDocument.cpp index 5b09605fc1..e3dba249e4 100644 --- a/modules/juce_core/native/juce_android_AndroidDocument.cpp +++ b/modules/juce_core/native/juce_android_AndroidDocument.cpp @@ -417,14 +417,18 @@ struct AndroidDocument::Utils return false; } - std::unique_ptr createInputStream() const override + std::unique_ptr createInputStream() const override { - return makeStream (AndroidStreamHelpers::StreamKind::input); + auto result = std::make_unique (uri); + return result->openedSuccessfully() ? std::move (result) : nullptr; } std::unique_ptr createOutputStream() const override { - return makeStream (AndroidStreamHelpers::StreamKind::output); + auto stream = AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::output); + + return stream.get() != nullptr ? std::make_unique (std::move (stream)) + : nullptr; } AndroidDocumentInfo getInfo() const override @@ -507,15 +511,6 @@ struct AndroidDocument::Utils NativeInfo getNativeInfo() const override { return { uri }; } private: - template - std::unique_ptr makeStream (AndroidStreamHelpers::StreamKind kind) const - { - auto stream = AndroidStreamHelpers::createStream (uri, kind); - - return stream.get() != nullptr ? std::make_unique (std::move (stream)) - : nullptr; - } - GlobalRef uri; }; diff --git a/modules/juce_core/native/juce_android_Files.cpp b/modules/juce_core/native/juce_android_Files.cpp index c0364541b2..efd43534d2 100644 --- a/modules/juce_core/native/juce_android_Files.cpp +++ b/modules/juce_core/native/juce_android_Files.cpp @@ -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 createStream (const GlobalRef& uri, StreamKind kind) + { + auto* env = getEnv(); + auto contentResolver = AndroidContentUriResolver::getContentResolver(); + + if (contentResolver == nullptr) + return {}; + + return LocalRef (env->CallObjectMethod (contentResolver.get(), + kind == StreamKind::input ? ContentResolver.openInputStream + : ContentResolver.openOutputStream, + uri.get())); + } +}; + //============================================================================== struct AndroidContentUriInputStream : public InputStream { - explicit AndroidContentUriInputStream (LocalRef&& 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 (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 (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; diff --git a/modules/juce_core/native/juce_android_Network.cpp b/modules/juce_core/native/juce_android_Network.cpp index e9e11022de..f5a3afa89b 100644 --- a/modules/juce_core/native/juce_android_Network.cpp +++ b/modules/juce_core/native/juce_android_Network.cpp @@ -312,25 +312,6 @@ String URL::getFileName() const return toString (false).fromLastOccurrenceOf ("/", false, true); } -struct AndroidStreamHelpers -{ - enum class StreamKind { output, input }; - - static LocalRef createStream (const GlobalRef& uri, StreamKind kind) - { - auto* env = getEnv(); - auto contentResolver = AndroidContentUriResolver::getContentResolver(); - - if (contentResolver == nullptr) - return {}; - - return LocalRef (env->CallObjectMethod (contentResolver.get(), - kind == StreamKind::input ? ContentResolver.openInputStream - : ContentResolver.openOutputStream, - uri.get())); - } -}; - //============================================================================== class WebInputStream::Pimpl {