mirror of https://github.com/OpenRCT2/OpenRCT2.git
Android: Replace curl dependency with HttpAndroid Java implementation
This commit is contained in:
parent
723b9ed5bc
commit
221877fa33
|
@ -29,7 +29,6 @@ ExternalProject_Add(libs
|
|||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_BYPRODUCTS
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}curl${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}crypto${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbis${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbisfile${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
|
@ -62,7 +61,6 @@ ExternalProject_Add(libs
|
|||
add_custom_command(TARGET libs POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/*.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libcurl.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libcrypto.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libvorbis.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libvorbisfile.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
|
@ -142,12 +140,6 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
|
|||
)
|
||||
add_dependencies(ssl libs)
|
||||
|
||||
add_library(curl SHARED IMPORTED)
|
||||
set_target_properties(curl PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}curl${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(curl libs)
|
||||
|
||||
add_library(crypto SHARED IMPORTED)
|
||||
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}crypto${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
|
@ -255,7 +247,7 @@ file(GLOB_RECURSE OPENRCT2_CLI_SOURCES
|
|||
"${ORCT2_ROOT}/src/openrct2-cli/*.hpp")
|
||||
|
||||
add_library(openrct2 SHARED ${LIBOPENRCT2_SOURCES})
|
||||
target_link_libraries(openrct2 android stdc++ log dl SDL2 png z icu icuuc icudata curl crypto ssl)
|
||||
target_link_libraries(openrct2 android stdc++ log dl SDL2 png z icu icuuc icudata crypto ssl)
|
||||
|
||||
add_library(openrct2-ui SHARED ${OPENRCT2_GUI_SOURCES})
|
||||
target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2main speexdsp brotlicommon brotlidec bz2 freetype ogg vorbis vorbisfile FLAC)
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package io.openrct2;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class HttpAndroid {
|
||||
private static final String TAG = "HttpAndroid";
|
||||
|
||||
// Corresponding Java enum for the C++ 'Status' enum
|
||||
public enum Status {
|
||||
Invalid(0),
|
||||
Ok(200),
|
||||
NotFound(404);
|
||||
|
||||
private final int code;
|
||||
Status(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponding Java enum for the C++ 'Method' enum
|
||||
public enum Method {
|
||||
GET,
|
||||
POST,
|
||||
PUT
|
||||
}
|
||||
|
||||
// Java class equivalent to the C++ 'Response' struct
|
||||
public static class Response {
|
||||
Status status;
|
||||
String contentType;
|
||||
String body;
|
||||
Map<String, List<String>> headers;
|
||||
String error;
|
||||
|
||||
}
|
||||
|
||||
// Java class equivalent to the C++ 'Request' struct
|
||||
public static class Request {
|
||||
String url;
|
||||
Map<String, String> headers;
|
||||
Method method;
|
||||
String body;
|
||||
boolean forceIPv4;
|
||||
|
||||
public Request() {
|
||||
this.method = Method.GET; // Default method
|
||||
}
|
||||
}
|
||||
|
||||
public static Response request(Request request) {
|
||||
Response response = new Response();
|
||||
response.status = Status.Invalid;
|
||||
response.error = "Request failed";
|
||||
try {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
URL url = new URL(request.url);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod(request.method.toString());
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(10000);
|
||||
connection.setUseCaches(false);
|
||||
connection.setDoInput(true);
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
if (request.headers != null) {
|
||||
for (Map.Entry<String, String> entry : request.headers.entrySet()) {
|
||||
connection.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
connection.connect();
|
||||
if (request.body!= null) {
|
||||
OutputStream os = connection.getOutputStream();
|
||||
os.write(request.body.getBytes());
|
||||
os.flush();
|
||||
os.close();
|
||||
}
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode == 200) {
|
||||
inputStream = connection.getInputStream();
|
||||
} else {
|
||||
inputStream = connection.getErrorStream();
|
||||
}
|
||||
// Log
|
||||
Log.d(TAG, "Request: " + request.url + ", response code: " + responseCode);
|
||||
response.status = Status.Ok;
|
||||
response.contentType = connection.getContentType();
|
||||
response.headers = connection.getHeaderFields();
|
||||
response.body = IOUtils.toString(inputStream, "UTF-8");
|
||||
response.error = null;
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error while requesting " + request.url + ", error: " + e.getMessage(), e);
|
||||
response.error = e.getMessage();
|
||||
return response;
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error while requesting " + request.url, e);
|
||||
response.error = e.getMessage();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2024 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#if !defined(DISABLE_HTTP) && defined(__ANDROID__)
|
||||
|
||||
# include "Http.h"
|
||||
|
||||
# include "../Version.h"
|
||||
# include "../platform/Platform.h"
|
||||
|
||||
# include <SDL.h>
|
||||
# include <android/log.h>
|
||||
# include <jni.h>
|
||||
|
||||
# define OPENRCT2_USER_AGENT "OpenRCT2/" OPENRCT2_VERSION
|
||||
|
||||
namespace Http
|
||||
{
|
||||
Response Do(const Request& req)
|
||||
{
|
||||
std::map<std::string, std::string> headers = req.header;
|
||||
headers["User-Agent"] = OPENRCT2_USER_AGENT;
|
||||
// Lambda to convert jstring to string
|
||||
auto jstringToString = [](JNIEnv* env, jstring jstr) -> std::string {
|
||||
if (jstr == nullptr)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
const char* cstr = env->GetStringUTFChars(jstr, nullptr);
|
||||
std::string str = cstr;
|
||||
env->ReleaseStringUTFChars(jstr, cstr);
|
||||
return str;
|
||||
};
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
|
||||
// Create request object
|
||||
|
||||
jclass HttpAndroidClass = Platform::AndroidFindClass(env, "io/openrct2/HttpAndroid");
|
||||
jclass req_class = Platform::AndroidFindClass(env, "io/openrct2/HttpAndroid$Request");
|
||||
// New request object
|
||||
jobject jniRequest = env->NewObject(req_class, env->GetMethodID(req_class, "<init>", "()V"));
|
||||
// Create request's headers map
|
||||
jobject jniHeaders = env->NewObject(
|
||||
env->FindClass("java/util/HashMap"),
|
||||
env->GetMethodID(env->GetObjectClass(env->FindClass("java/util/HashMap")), "<init>", "()V"));
|
||||
// HashMap's put method
|
||||
jmethodID putMethod = env->GetMethodID(
|
||||
env->FindClass("java/util/HashMap"), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
for (const auto& header : headers)
|
||||
{
|
||||
jstring jniKey = env->NewStringUTF(header.first.c_str());
|
||||
jstring jniValue = env->NewStringUTF(header.second.c_str());
|
||||
env->CallObjectMethod(jniHeaders, putMethod, jniKey, jniValue);
|
||||
}
|
||||
env->SetObjectField(jniRequest, env->GetFieldID(req_class, "headers", "Ljava/util/Map;"), jniHeaders);
|
||||
|
||||
jstring req_body = req.body.empty() ? nullptr : env->NewStringUTF(req.body.c_str());
|
||||
jstring url = req.url.empty() ? nullptr : env->NewStringUTF(req.url.c_str());
|
||||
env->SetObjectField(jniRequest, env->GetFieldID(req_class, "body", "Ljava/lang/String;"), req_body);
|
||||
env->SetObjectField(jniRequest, env->GetFieldID(req_class, "url", "Ljava/lang/String;"), url);
|
||||
if (req_body != nullptr)
|
||||
{
|
||||
env->ReleaseStringUTFChars(req_body, env->GetStringUTFChars(req_body, nullptr));
|
||||
}
|
||||
if (url != nullptr)
|
||||
{
|
||||
env->ReleaseStringUTFChars(url, env->GetStringUTFChars(url, nullptr));
|
||||
}
|
||||
std::string method = "GET";
|
||||
switch (req.method)
|
||||
{
|
||||
case Method::GET:
|
||||
method = "GET";
|
||||
break;
|
||||
case Method::POST:
|
||||
method = "POST";
|
||||
break;
|
||||
case Method::PUT:
|
||||
method = "PUT";
|
||||
break;
|
||||
}
|
||||
env->SetObjectField(
|
||||
jniRequest, env->GetFieldID(req_class, "method", "Lio/openrct2/HttpAndroid$Method;"),
|
||||
env->GetStaticObjectField(
|
||||
Platform::AndroidFindClass(env, "io/openrct2/HttpAndroid$Method"),
|
||||
env->GetStaticFieldID(
|
||||
Platform::AndroidFindClass(env, "io/openrct2/HttpAndroid$Method"), method.c_str(),
|
||||
"Lio/openrct2/HttpAndroid$Method;")));
|
||||
// Call request method
|
||||
jmethodID requestMethod = env->GetStaticMethodID(
|
||||
HttpAndroidClass, "request", "(Lio/openrct2/HttpAndroid$Request;)Lio/openrct2/HttpAndroid$Response;");
|
||||
jobject jniResponse = env->CallStaticObjectMethod(HttpAndroidClass, requestMethod, jniRequest);
|
||||
jfieldID statusField = env->GetFieldID(env->GetObjectClass(jniResponse), "status", "Lio/openrct2/HttpAndroid$Status;");
|
||||
jobject jniStatus = env->GetObjectField(jniResponse, statusField);
|
||||
jclass statusClass = Platform::AndroidFindClass(env, "io/openrct2/HttpAndroid$Status");
|
||||
jmethodID getCodeMethod = env->GetMethodID(statusClass, "getCode", "()I");
|
||||
int code = env->CallIntMethod(jniStatus, getCodeMethod);
|
||||
|
||||
Response response;
|
||||
|
||||
// Get response's headers field
|
||||
jclass responseClass = env->GetObjectClass(jniResponse);
|
||||
jfieldID headersField = env->GetFieldID(responseClass, "headers", "Ljava/util/Map;");
|
||||
jobject jniHeadersMap = env->GetObjectField(jniResponse, headersField);
|
||||
|
||||
jmethodID entrySetMethod = env->GetMethodID(env->GetObjectClass(jniHeadersMap), "entrySet", "()Ljava/util/Set;");
|
||||
jobject jniEntrySet = env->CallObjectMethod(jniHeadersMap, entrySetMethod);
|
||||
jmethodID iteratorMethod = env->GetMethodID(env->GetObjectClass(jniEntrySet), "iterator", "()Ljava/util/Iterator;");
|
||||
jobject jniIterator = env->CallObjectMethod(jniEntrySet, iteratorMethod);
|
||||
jmethodID hasNextMethod = env->GetMethodID(env->GetObjectClass(jniIterator), "hasNext", "()Z");
|
||||
jmethodID nextMethod = env->GetMethodID(env->GetObjectClass(jniIterator), "next", "()Ljava/lang/Object;");
|
||||
while (env->CallBooleanMethod(jniIterator, hasNextMethod))
|
||||
{
|
||||
jobject jniNext = env->CallObjectMethod(jniIterator, nextMethod);
|
||||
jmethodID getKeyMethod = env->GetMethodID(env->GetObjectClass(jniNext), "getKey", "()Ljava/lang/Object;");
|
||||
jmethodID getValueMethod = env->GetMethodID(env->GetObjectClass(jniNext), "getValue", "()Ljava/lang/Object;");
|
||||
jobject jniKey = env->CallObjectMethod(jniNext, getKeyMethod);
|
||||
// The first key is always null for some reason, its value contains the status code
|
||||
if (jniKey == nullptr)
|
||||
{
|
||||
env->DeleteLocalRef(jniKey);
|
||||
env->DeleteLocalRef(jniNext);
|
||||
continue;
|
||||
}
|
||||
jobject jniValue = env->CallObjectMethod(jniNext, getValueMethod);
|
||||
// convert jniValue to string
|
||||
jmethodID valueToStringMethod = env->GetMethodID(env->GetObjectClass(jniValue), "toString", "()Ljava/lang/String;");
|
||||
jstring jniValueString = (jstring)env->CallObjectMethod(jniValue, valueToStringMethod);
|
||||
jstring jniKeyString = (jstring)jniKey;
|
||||
const char* jniKeyChars = env->GetStringUTFChars(jniKeyString, nullptr);
|
||||
const char* jniValueChars = env->GetStringUTFChars(jniValueString, nullptr);
|
||||
std::string value = jniValueChars;
|
||||
std::string key = jniKeyChars;
|
||||
env->ReleaseStringUTFChars(jniKeyString, jniKeyChars);
|
||||
env->ReleaseStringUTFChars(jniValueString, jniValueChars);
|
||||
response.header[key] = value;
|
||||
|
||||
// Cleanup
|
||||
env->DeleteLocalRef(jniValue);
|
||||
env->DeleteLocalRef(jniKey);
|
||||
env->DeleteLocalRef(jniNext);
|
||||
}
|
||||
// Get response's body field and convert it to string
|
||||
jfieldID bodyField = env->GetFieldID(responseClass, "body", "Ljava/lang/String;");
|
||||
jstring jniBody = (jstring)env->GetObjectField(jniResponse, bodyField);
|
||||
// Get response's error field and convert it to string
|
||||
jfieldID errorField = env->GetFieldID(responseClass, "error", "Ljava/lang/String;");
|
||||
jstring jniError = (jstring)env->GetObjectField(jniResponse, errorField);
|
||||
// Get response's content type field and convert it to string
|
||||
jfieldID contentTypeField = env->GetFieldID(responseClass, "contentType", "Ljava/lang/String;");
|
||||
jstring jniContentType = (jstring)env->GetObjectField(jniResponse, contentTypeField);
|
||||
// Prepare response
|
||||
response.error = jstringToString(env, jniError);
|
||||
response.content_type = jstringToString(env, jniContentType);
|
||||
response.status = static_cast<Status>(code);
|
||||
response.body = jstringToString(env, jniBody);
|
||||
|
||||
env->DeleteLocalRef(jniResponse);
|
||||
env->DeleteLocalRef(jniStatus);
|
||||
env->DeleteLocalRef(jniHeadersMap);
|
||||
env->DeleteLocalRef(jniBody);
|
||||
env->DeleteLocalRef(jniContentType);
|
||||
env->DeleteLocalRef(jniError);
|
||||
env->DeleteLocalRef(jniHeaders);
|
||||
env->DeleteLocalRef(jniRequest);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace Http
|
||||
|
||||
#endif
|
|
@ -7,7 +7,7 @@
|
|||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#if !defined(DISABLE_HTTP) && !defined(_WIN32)
|
||||
#if !defined(DISABLE_HTTP) && !defined(_WIN32) && !defined(__ANDROID__)
|
||||
|
||||
# include "Http.h"
|
||||
|
||||
|
|
Loading…
Reference in New Issue