mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
Android: Add AndroidHTTPDownloader class
This commit is contained in:
parent
566ecaf209
commit
6bced299f4
|
@ -3,6 +3,8 @@ set(SRCS
|
|||
android_controller_interface.h
|
||||
android_host_interface.cpp
|
||||
android_host_interface.h
|
||||
android_http_downloader.cpp
|
||||
android_http_downloader.h
|
||||
android_progress_callback.cpp
|
||||
android_progress_callback.h
|
||||
android_settings_interface.cpp
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
#include "frontend-common/controller_interface.h"
|
||||
#include "core/types.h"
|
||||
#include "frontend-common/controller_interface.h"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
|
|
@ -55,6 +55,11 @@ static jclass s_SaveStateInfo_class;
|
|||
static jmethodID s_SaveStateInfo_constructor;
|
||||
|
||||
namespace AndroidHelpers {
|
||||
JavaVM* GetJavaVM()
|
||||
{
|
||||
return s_jvm;
|
||||
}
|
||||
|
||||
// helper for retrieving the current per-thread jni environment
|
||||
JNIEnv* GetJNIEnv()
|
||||
{
|
||||
|
|
|
@ -109,6 +109,7 @@ private:
|
|||
|
||||
namespace AndroidHelpers {
|
||||
|
||||
JavaVM* GetJavaVM();
|
||||
JNIEnv* GetJNIEnv();
|
||||
AndroidHostInterface* GetNativeClass(JNIEnv* env, jobject obj);
|
||||
std::string JStringToString(JNIEnv* env, jstring str);
|
||||
|
|
162
android/app/src/cpp/android_http_downloader.cpp
Normal file
162
android/app/src/cpp/android_http_downloader.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
#include "android_http_downloader.h"
|
||||
#include "android_host_interface.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
Log_SetChannel(AndroidHTTPDownloader);
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
AndroidHTTPDownloader::AndroidHTTPDownloader() : HTTPDownloader() {}
|
||||
|
||||
AndroidHTTPDownloader::~AndroidHTTPDownloader()
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
if (m_URLDownloader_class)
|
||||
env->DeleteGlobalRef(m_URLDownloader_class);
|
||||
}
|
||||
|
||||
std::unique_ptr<HTTPDownloader> HTTPDownloader::Create()
|
||||
{
|
||||
std::unique_ptr<AndroidHTTPDownloader> instance(std::make_unique<AndroidHTTPDownloader>());
|
||||
if (!instance->Initialize())
|
||||
return {};
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool AndroidHTTPDownloader::Initialize()
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jclass klass = env->FindClass("com/github/stenzek/duckstation/URLDownloader");
|
||||
if (!klass)
|
||||
return false;
|
||||
|
||||
m_URLDownloader_class = static_cast<jclass>(env->NewGlobalRef(klass));
|
||||
if (!m_URLDownloader_class)
|
||||
return false;
|
||||
|
||||
m_URLDownloader_constructor = env->GetMethodID(klass, "<init>", "()V");
|
||||
m_URLDownloader_get = env->GetMethodID(klass, "get", "(Ljava/lang/String;)Z");
|
||||
m_URLDownloader_post = env->GetMethodID(klass, "post", "(Ljava/lang/String;[B)Z");
|
||||
m_URLDownloader_getStatusCode = env->GetMethodID(klass, "getStatusCode", "()I");
|
||||
m_URLDownloader_getData = env->GetMethodID(klass, "getData", "()[B");
|
||||
if (!m_URLDownloader_constructor || !m_URLDownloader_get || !m_URLDownloader_post || !m_URLDownloader_getStatusCode ||
|
||||
!m_URLDownloader_getData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_thread_pool = std::make_unique<cb::ThreadPool>(m_max_active_requests);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AndroidHTTPDownloader::ProcessRequest(Request* req)
|
||||
{
|
||||
std::unique_lock<std::mutex> cancel_lock(m_cancel_mutex);
|
||||
if (req->closed.load())
|
||||
return;
|
||||
|
||||
cancel_lock.unlock();
|
||||
req->status_code = -1;
|
||||
req->start_time = Common::Timer::GetValue();
|
||||
|
||||
// TODO: Move to Java side...
|
||||
JNIEnv* env;
|
||||
if (AndroidHelpers::GetJavaVM()->AttachCurrentThread(&env, nullptr) == JNI_OK)
|
||||
{
|
||||
jobject obj = env->NewObject(m_URLDownloader_class, m_URLDownloader_constructor);
|
||||
jstring url_string = env->NewStringUTF(req->url.c_str());
|
||||
jboolean result;
|
||||
if (req->post_data.empty())
|
||||
{
|
||||
result = env->CallBooleanMethod(obj, m_URLDownloader_get, url_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
jbyteArray post_data = env->NewByteArray(static_cast<jsize>(req->post_data.size()));
|
||||
env->SetByteArrayRegion(post_data, 0, static_cast<jsize>(req->post_data.size()),
|
||||
reinterpret_cast<const jbyte*>(req->post_data.data()));
|
||||
result = env->CallBooleanMethod(obj, m_URLDownloader_post, url_string, post_data);
|
||||
env->DeleteLocalRef(post_data);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(url_string);
|
||||
|
||||
if (result)
|
||||
{
|
||||
req->status_code = env->CallIntMethod(obj, m_URLDownloader_getStatusCode);
|
||||
|
||||
jbyteArray data = reinterpret_cast<jbyteArray>(env->CallObjectMethod(obj, m_URLDownloader_getData));
|
||||
if (data)
|
||||
{
|
||||
const u32 size = static_cast<u32>(env->GetArrayLength(data));
|
||||
req->data.resize(size);
|
||||
if (size > 0)
|
||||
{
|
||||
jbyte* data_ptr = env->GetByteArrayElements(data, nullptr);
|
||||
std::memcpy(req->data.data(), data_ptr, size);
|
||||
env->ReleaseByteArrayElements(data, data_ptr, 0);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(data);
|
||||
}
|
||||
|
||||
Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", req->url.c_str(), req->status_code,
|
||||
req->data.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Request for '%s' failed", req->url.c_str());
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("AttachCurrentThread() failed");
|
||||
}
|
||||
|
||||
cancel_lock.lock();
|
||||
req->state = Request::State::Complete;
|
||||
if (req->closed.load())
|
||||
delete req;
|
||||
else
|
||||
req->closed.store(true);
|
||||
}
|
||||
|
||||
HTTPDownloader::Request* AndroidHTTPDownloader::InternalCreateRequest()
|
||||
{
|
||||
Request* req = new Request();
|
||||
return req;
|
||||
}
|
||||
|
||||
void AndroidHTTPDownloader::InternalPollRequests()
|
||||
{
|
||||
// noop - uses thread pool
|
||||
}
|
||||
|
||||
bool AndroidHTTPDownloader::StartRequest(HTTPDownloader::Request* request)
|
||||
{
|
||||
Request* req = static_cast<Request*>(request);
|
||||
Log_DevPrintf("Started HTTP request for '%s'", req->url.c_str());
|
||||
req->state = Request::State::Started;
|
||||
req->start_time = Common::Timer::GetValue();
|
||||
m_thread_pool->Schedule(std::bind(&AndroidHTTPDownloader::ProcessRequest, this, req));
|
||||
return true;
|
||||
}
|
||||
|
||||
void AndroidHTTPDownloader::CloseRequest(HTTPDownloader::Request* request)
|
||||
{
|
||||
std::unique_lock<std::mutex> cancel_lock(m_cancel_mutex);
|
||||
Request* req = static_cast<Request*>(request);
|
||||
if (req->closed.load())
|
||||
delete req;
|
||||
else
|
||||
req->closed.store(true);
|
||||
}
|
||||
|
||||
} // namespace FrontendCommon
|
44
android/app/src/cpp/android_http_downloader.h
Normal file
44
android/app/src/cpp/android_http_downloader.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
#include "common/thirdparty/thread_pool.h"
|
||||
#include "frontend-common/http_downloader.h"
|
||||
#include <atomic>
|
||||
#include <jni.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace FrontendCommon {
|
||||
|
||||
class AndroidHTTPDownloader final : public HTTPDownloader
|
||||
{
|
||||
public:
|
||||
AndroidHTTPDownloader();
|
||||
~AndroidHTTPDownloader() override;
|
||||
|
||||
bool Initialize();
|
||||
|
||||
protected:
|
||||
Request* InternalCreateRequest() override;
|
||||
void InternalPollRequests() override;
|
||||
bool StartRequest(HTTPDownloader::Request* request) override;
|
||||
void CloseRequest(HTTPDownloader::Request* request) override;
|
||||
|
||||
private:
|
||||
struct Request : HTTPDownloader::Request
|
||||
{
|
||||
std::atomic_bool closed{false};
|
||||
};
|
||||
|
||||
void ProcessRequest(Request* req);
|
||||
|
||||
std::unique_ptr<cb::ThreadPool> m_thread_pool;
|
||||
std::mutex m_cancel_mutex;
|
||||
|
||||
jclass m_URLDownloader_class = nullptr;
|
||||
jmethodID m_URLDownloader_constructor = nullptr;
|
||||
jmethodID m_URLDownloader_get = nullptr;
|
||||
jmethodID m_URLDownloader_post = nullptr;
|
||||
jmethodID m_URLDownloader_getStatusCode = nullptr;
|
||||
jmethodID m_URLDownloader_getData = nullptr;
|
||||
};
|
||||
|
||||
} // namespace FrontendCommon
|
|
@ -1,11 +1,10 @@
|
|||
#include "android_progress_callback.h"
|
||||
#include "android_host_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
Log_SetChannel(AndroidProgressCallback);
|
||||
|
||||
AndroidProgressCallback::AndroidProgressCallback(JNIEnv* env, jobject java_object)
|
||||
: m_java_object(java_object)
|
||||
AndroidProgressCallback::AndroidProgressCallback(JNIEnv* env, jobject java_object) : m_java_object(java_object)
|
||||
{
|
||||
jclass cls = env->GetObjectClass(java_object);
|
||||
m_set_title_method = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
|
||||
|
@ -15,7 +14,8 @@ AndroidProgressCallback::AndroidProgressCallback(JNIEnv* env, jobject java_objec
|
|||
m_modal_error_method = env->GetMethodID(cls, "modalError", "(Ljava/lang/String;)V");
|
||||
m_modal_information_method = env->GetMethodID(cls, "modalInformation", "(Ljava/lang/String;)V");
|
||||
m_modal_confirmation_method = env->GetMethodID(cls, "modalConfirmation", "(Ljava/lang/String;)Z");
|
||||
Assert(m_set_status_text_method && m_set_progress_range_method && m_set_progress_value_method && m_modal_error_method && m_modal_information_method && m_modal_confirmation_method);
|
||||
Assert(m_set_status_text_method && m_set_progress_range_method && m_set_progress_value_method &&
|
||||
m_modal_error_method && m_modal_information_method && m_modal_confirmation_method);
|
||||
}
|
||||
|
||||
AndroidProgressCallback::~AndroidProgressCallback() = default;
|
||||
|
|
|
@ -56,16 +56,25 @@ AndroidSettingsInterface::AndroidSettingsInterface(jobject java_context)
|
|||
Assert(m_get_boolean && m_get_int && m_get_float && m_get_string && m_get_string_set && m_set_to_array);
|
||||
|
||||
m_edit = env->GetMethodID(m_shared_preferences_class, "edit", "()Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_set_string = env->GetMethodID(m_shared_preferences_editor_class, "putString", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_set_string =
|
||||
env->GetMethodID(m_shared_preferences_editor_class, "putString",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_commit = env->GetMethodID(m_shared_preferences_editor_class, "commit", "()Z");
|
||||
m_edit_remove = env->GetMethodID(m_shared_preferences_editor_class, "remove", "(Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_remove = env->GetMethodID(m_shared_preferences_editor_class, "remove",
|
||||
"(Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
Assert(m_edit && m_edit_set_string && m_edit_commit && m_edit_remove);
|
||||
|
||||
m_helper_clear_section = env->GetStaticMethodID(m_helper_class, "clearSection", "(Landroid/content/SharedPreferences;Ljava/lang/String;)V");
|
||||
m_helper_add_to_string_list = env->GetStaticMethodID(m_helper_class, "addToStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_remove_from_string_list = env->GetStaticMethodID(m_helper_class, "removeFromStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_set_string_list = env->GetStaticMethodID(m_helper_class, "setStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
Assert(m_helper_clear_section && m_helper_add_to_string_list && m_helper_remove_from_string_list && m_helper_set_string_list);
|
||||
m_helper_clear_section =
|
||||
env->GetStaticMethodID(m_helper_class, "clearSection", "(Landroid/content/SharedPreferences;Ljava/lang/String;)V");
|
||||
m_helper_add_to_string_list = env->GetStaticMethodID(
|
||||
m_helper_class, "addToStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_remove_from_string_list =
|
||||
env->GetStaticMethodID(m_helper_class, "removeFromStringList",
|
||||
"(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_set_string_list = env->GetStaticMethodID(
|
||||
m_helper_class, "setStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
Assert(m_helper_clear_section && m_helper_add_to_string_list && m_helper_remove_from_string_list &&
|
||||
m_helper_set_string_list);
|
||||
}
|
||||
|
||||
AndroidSettingsInterface::~AndroidSettingsInterface()
|
||||
|
@ -212,7 +221,7 @@ jobject AndroidSettingsInterface::GetPreferencesEditor(JNIEnv* env)
|
|||
return env->CallObjectMethod(m_java_shared_preferences, m_edit);
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::CheckForException(JNIEnv *env, const char *task)
|
||||
void AndroidSettingsInterface::CheckForException(JNIEnv* env, const char* task)
|
||||
{
|
||||
if (!env->ExceptionCheck())
|
||||
return;
|
||||
|
@ -230,7 +239,8 @@ void AndroidSettingsInterface::SetIntValue(const char* section, const char* key,
|
|||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(TinyString::FromFormat("%d", value)));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
LocalRefHolder<jobject> dummy(env,
|
||||
env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetIntValue");
|
||||
|
@ -245,7 +255,8 @@ void AndroidSettingsInterface::SetFloatValue(const char* section, const char* ke
|
|||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(TinyString::FromFormat("%f", value)));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
LocalRefHolder<jobject> dummy(env,
|
||||
env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetFloatValue");
|
||||
|
@ -260,7 +271,8 @@ void AndroidSettingsInterface::SetBoolValue(const char* section, const char* key
|
|||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(value ? "true" : "false"));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
LocalRefHolder<jobject> dummy(env,
|
||||
env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetBoolValue");
|
||||
|
@ -275,7 +287,8 @@ void AndroidSettingsInterface::SetStringValue(const char* section, const char* k
|
|||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(value));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
LocalRefHolder<jobject> dummy(env,
|
||||
env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetStringValue");
|
||||
|
@ -316,10 +329,11 @@ std::vector<std::string> AndroidSettingsInterface::GetStringList(const char* sec
|
|||
env->ExceptionClear();
|
||||
|
||||
// this might just be a string, not a string set
|
||||
LocalRefHolder<jstring> string_object(
|
||||
env, reinterpret_cast<jstring>(env->CallObjectMethod(m_java_shared_preferences, m_get_string, key_string.Get(), nullptr)));
|
||||
LocalRefHolder<jstring> string_object(env, reinterpret_cast<jstring>(env->CallObjectMethod(
|
||||
m_java_shared_preferences, m_get_string, key_string.Get(), nullptr)));
|
||||
|
||||
if (!env->ExceptionCheck()) {
|
||||
if (!env->ExceptionCheck())
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
if (string_object)
|
||||
ret.push_back(AndroidHelpers::JStringToString(env, string_object));
|
||||
|
@ -369,7 +383,8 @@ void AndroidSettingsInterface::SetStringList(const char* section, const char* ke
|
|||
}
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobjectArray> items_array(env, env->NewObjectArray(static_cast<jsize>(items.size()), AndroidHelpers::GetStringClass(), nullptr));
|
||||
LocalRefHolder<jobjectArray> items_array(
|
||||
env, env->NewObjectArray(static_cast<jsize>(items.size()), AndroidHelpers::GetStringClass(), nullptr));
|
||||
for (size_t i = 0; i < items.size(); i++)
|
||||
{
|
||||
LocalRefHolder<jstring> item_jstr(env, env->NewStringUTF(items[i].c_str()));
|
||||
|
@ -377,7 +392,8 @@ void AndroidSettingsInterface::SetStringList(const char* section, const char* ke
|
|||
}
|
||||
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
env->CallStaticVoidMethod(m_helper_class, m_helper_set_string_list, m_java_shared_preferences, key_string.Get(), items_array.Get());
|
||||
env->CallStaticVoidMethod(m_helper_class, m_helper_set_string_list, m_java_shared_preferences, key_string.Get(),
|
||||
items_array.Get());
|
||||
|
||||
CheckForException(env, "SetStringList");
|
||||
}
|
||||
|
@ -389,7 +405,8 @@ bool AndroidSettingsInterface::RemoveFromStringList(const char* section, const c
|
|||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> item_string(env, env->NewStringUTF(item));
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_remove_from_string_list, m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_remove_from_string_list,
|
||||
m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
CheckForException(env, "RemoveFromStringList");
|
||||
return result;
|
||||
}
|
||||
|
@ -401,7 +418,8 @@ bool AndroidSettingsInterface::AddToStringList(const char* section, const char*
|
|||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> item_string(env, env->NewStringUTF(item));
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_add_to_string_list, m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_add_to_string_list,
|
||||
m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
CheckForException(env, "AddToStringList");
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -129,8 +129,8 @@ bool OpenSLESAudioStream::OpenDevice()
|
|||
for (u32 i = 0; i < NUM_BUFFERS; i++)
|
||||
m_buffers[i] = std::make_unique<SampleType[]>(m_buffer_size * m_channels);
|
||||
|
||||
Log_InfoPrintf("OpenSL ES device opened: %uhz, %u channels, %u buffer size, %u buffers",
|
||||
m_output_sample_rate, m_channels, m_buffer_size, NUM_BUFFERS);
|
||||
Log_InfoPrintf("OpenSL ES device opened: %uhz, %u channels, %u buffer size, %u buffers", m_output_sample_rate,
|
||||
m_channels, m_buffer_size, NUM_BUFFERS);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,8 @@ void OpenSLESAudioStream::PauseDevice(bool paused)
|
|||
if (m_paused == paused)
|
||||
return;
|
||||
|
||||
SLresult res = (*m_play_interface)->SetPlayState(m_play_interface, paused ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING);
|
||||
SLresult res =
|
||||
(*m_play_interface)->SetPlayState(m_play_interface, paused ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING);
|
||||
if (res != SL_RESULT_SUCCESS)
|
||||
Log_ErrorPrintf("SetPlayState failed: %d", res);
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Helper class for exposing HTTP downloads to native code without pulling in an external
|
||||
* dependency for doing so.
|
||||
*/
|
||||
public class URLDownloader {
|
||||
private int statusCode = -1;
|
||||
private byte[] data = null;
|
||||
|
||||
public URLDownloader() {
|
||||
}
|
||||
|
||||
static private HttpURLConnection getConnection(String url) {
|
||||
try {
|
||||
final URL parsedUrl = new URL(url);
|
||||
HttpURLConnection connection = (HttpURLConnection) parsedUrl.openConnection();
|
||||
if (connection == null)
|
||||
throw new RuntimeException(String.format("openConnection(%s) returned null", url));
|
||||
|
||||
return connection;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean download(HttpURLConnection connection) {
|
||||
try {
|
||||
statusCode = connection.getResponseCode();
|
||||
if (statusCode != HttpURLConnection.HTTP_OK)
|
||||
return false;
|
||||
|
||||
final InputStream inStream = new BufferedInputStream(connection.getInputStream());
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
final int CHUNK_SIZE = 128 * 1024;
|
||||
final byte[] chunk = new byte[CHUNK_SIZE];
|
||||
int len;
|
||||
while ((len = inStream.read(chunk)) > 0) {
|
||||
outputStream.write(chunk, 0, len);
|
||||
}
|
||||
|
||||
data = outputStream.toByteArray();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean get(String url) {
|
||||
final HttpURLConnection connection = getConnection(url);
|
||||
if (connection == null)
|
||||
return false;
|
||||
|
||||
return download(connection);
|
||||
}
|
||||
|
||||
public boolean post(String url, byte[] postData) {
|
||||
final HttpURLConnection connection = getConnection(url);
|
||||
if (connection == null)
|
||||
return false;
|
||||
|
||||
try {
|
||||
connection.setDoOutput(true);
|
||||
connection.setChunkedStreamingMode(0);
|
||||
|
||||
OutputStream postStream = new BufferedOutputStream(connection.getOutputStream());
|
||||
postStream.write(postData);
|
||||
return download(connection);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -100,14 +100,14 @@ if(ENABLE_CHEEVOS)
|
|||
http_downloader_winhttp.cpp
|
||||
http_downloader_winhttp.h
|
||||
)
|
||||
else()
|
||||
target_sources(frontend-common PRIVATE
|
||||
http_downloader_curl.cpp
|
||||
http_downloader_curl.h
|
||||
)
|
||||
target_link_libraries(frontend-common PRIVATE
|
||||
CURL::libcurl
|
||||
)
|
||||
elseif(NOT ANDROID)
|
||||
target_sources(frontend-common PRIVATE
|
||||
http_downloader_curl.cpp
|
||||
http_downloader_curl.h
|
||||
)
|
||||
target_link_libraries(frontend-common PRIVATE
|
||||
CURL::libcurl
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(frontend-common PRIVATE
|
||||
|
|
Loading…
Reference in a new issue