Merge pull request #21631 from janisozaur/android-enable-more-libraries

Android: enable more libraries
This commit is contained in:
Michał Janiszewski 2024-03-21 14:22:36 +01:00 committed by GitHub
commit 4484dc647c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 377 additions and 9 deletions

View File

@ -1,6 +1,7 @@
0.4.10 (in development)
------------------------------------------------------------------------
- Improved: [#21424] Extra viewports can now rotate independently from the main viewport.
- Improved: [#21561, #21631] Enable more features in Android build (plugins, networking, multiplayer, audio formats).
- Improved: [#21599] Currency signs now use non-breaking spaces.
- Change: [#21529] Classify “Southern Sands”, “Tiny Towers”, “Nevermore Park”, “Pacifica” as expert scenarios.
- Fix: [#910] Extra viewport does not preserve the location when rotating.
@ -67,7 +68,7 @@
- Fix: [#21039] Text rendering bleeds pixels through windows.
- Fix: [#21054] “No entrance” style is selected by default in the track designer.
- Fix: [#21145] [Plugin] setInterval/setTimeout handle conflict.
- Fix: [#21157] [Plugin] Widgets do not redraw correctly when updating disabled or visibility state.
- Fix: [#21157] [Plugin] Widgets do not redraw correctly when updating disabled or visibility state.
- Fix: [#21158] [Plugin] Potential crash using setInterval/setTimeout within the callback.
- Fix: [#21171] [Plugin] Crash creating entities with no more entity slots available.
- Fix: [#21178] Inca Lost Citys scenario description incorrectly states there are height restrictions.

View File

@ -17,7 +17,7 @@ android {
arguments '-DANDROID_STL=c++_shared'
targets 'openrct2', 'openrct2-ui', 'openrct2-cli'
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
abiFilters 'arm64-v8a'
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
@ -45,8 +45,8 @@ android {
}
dependencies {
implementation 'commons-io:commons-io:2.6'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'commons-io:commons-io:2.13.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation fileTree(include: ['*.jar'], dir: 'libs')
}

View File

@ -20,7 +20,7 @@ endif()
include(ExternalProject)
ExternalProject_Add(libs
URL https://github.com/OpenRCT2/openrct2-dependencies-android/releases/download/v10/${ANDROID_ABI}-android-dynamic.tar.zst
URL https://github.com/OpenRCT2/openrct2-dependencies-android/releases/download/v11/${ANDROID_ABI}-android-dynamic.tar.zst
SOURCE_DIR "${CMAKE_BINARY_DIR}/libs"
@ -29,6 +29,14 @@ ExternalProject_Add(libs
INSTALL_COMMAND ""
BUILD_BYPRODUCTS
${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}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}FLAC${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlidec${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlicommon${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ogg${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}bz2${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}freetype${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}png16${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2${CMAKE_SHARED_LIBRARY_SUFFIX}
@ -53,6 +61,14 @@ 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/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}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libFLAC.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbrotlidec.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbrotlicommon.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libogg.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbz2.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicudata.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicuuc.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicui18n.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
@ -124,13 +140,61 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
)
add_dependencies(ssl 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}
)
add_dependencies(crypto libs)
add_library(vorbis SHARED IMPORTED)
set_target_properties(vorbis PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbis${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(vorbis libs)
add_library(vorbisfile SHARED IMPORTED)
set_target_properties(vorbisfile PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbisfile${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(vorbisfile libs)
add_library(FLAC SHARED IMPORTED)
set_target_properties(FLAC PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}FLAC${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(FLAC libs)
add_library(brotlidec SHARED IMPORTED)
set_target_properties(brotlidec PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlidec${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(brotlidec libs)
add_library(brotlicommon SHARED IMPORTED)
set_target_properties(brotlicommon PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlicommon${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(brotlicommon libs)
add_library(ogg SHARED IMPORTED)
set_target_properties(ogg PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ogg${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(ogg libs)
add_library(bz2 SHARED IMPORTED)
set_target_properties(bz2 PROPERTIES IMPORTED_LOCATION
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}bz2${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_dependencies(bz2 libs)
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include")
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include/freetype2")
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include/SDL2")
# now build app's shared lib
add_definitions(-DDISABLE_DISCORD_RPC -DDISABLE_HTTP -DDISABLE_NETWORK -DDISABLE_FLAC -DDISABLE_VORBIS -DDISABLE_OPENGL -DGL_GLEXT_PROTOTYPES -D__STDC_LIMIT_MACROS -DNO_TTF -DSDL_MAIN_HANDLED)
add_definitions(-DDISABLE_DISCORD_RPC -DDISABLE_OPENGL -DGL_GLEXT_PROTOTYPES -D__STDC_LIMIT_MACROS -DNO_TTF -DSDL_MAIN_HANDLED)
# Enable scripting
add_definitions(-DENABLE_SCRIPTING)
@ -183,10 +247,10 @@ 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)
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)
target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2main speexdsp brotlicommon brotlidec bz2 freetype ogg vorbis vorbisfile FLAC)
add_executable(openrct2-cli ${OPENRCT2_CLI_SOURCES})
target_link_libraries(openrct2-cli openrct2 android stdc++ GLESv1_CM GLESv2)

View File

@ -80,6 +80,8 @@ public class GameActivity extends SDLActivity {
return new String[]{
"c++_shared",
"speexdsp",
"bz2",
"freetype",
"z",
"png16",
"SDL2",

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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"