mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-08-13 11:54:04 +02:00
Implement Platform::DefaultDdir for android
The default directory is acquired via getExternalFilesDir. Typical value: /storage/emulated/0/Android/data/uk.co.powdertoy.tpt/files, which seems to be user-accessible both on old and new Android. Probably. Hopefully. We'll see.
This commit is contained in:
@@ -10,7 +10,7 @@ import java.util.Base64;
|
||||
|
||||
public class PowderActivity extends SDLActivity
|
||||
{
|
||||
public static String getCertificateBundle()
|
||||
public String getCertificateBundle()
|
||||
{
|
||||
String allPems = "";
|
||||
try {
|
||||
@@ -39,4 +39,9 @@ public class PowderActivity extends SDLActivity
|
||||
}
|
||||
return allPems;
|
||||
}
|
||||
|
||||
public String getDefaultDdir()
|
||||
{
|
||||
return getExternalFilesDir(null).getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
@@ -1,84 +1,34 @@
|
||||
#include "CurlError.h"
|
||||
#include "common/String.h"
|
||||
#include "common/platform/Android.h"
|
||||
#include "Config.h"
|
||||
#include <iostream>
|
||||
#include <SDL.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
static jclass FindClass(JNIEnv *env, const char *name)
|
||||
{
|
||||
jobject nativeActivity = (jobject)SDL_AndroidGetActivity(); if (!nativeActivity) return NULL;
|
||||
jclass acl = env->GetObjectClass(nativeActivity); if (!acl ) return NULL;
|
||||
jmethodID getClassLoader = env->GetMethodID(acl, "getClassLoader", "()Ljava/lang/ClassLoader;"); if (!getClassLoader) return NULL;
|
||||
jobject cls = env->CallObjectMethod(nativeActivity, getClassLoader); if (!cls ) return NULL;
|
||||
jclass classLoader = env->FindClass("java/lang/ClassLoader"); if (!classLoader ) return NULL;
|
||||
jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if (!findClass ) return NULL;
|
||||
jstring strClassName = env->NewStringUTF(name); if (!strClassName ) return NULL;
|
||||
jclass clazz = (jclass)(env->CallObjectMethod(cls, findClass, strClassName));
|
||||
env->DeleteLocalRef(strClassName);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
namespace http
|
||||
{
|
||||
void UseSystemCertProvider(CURL *easy)
|
||||
{
|
||||
struct DoOnce
|
||||
{
|
||||
ByteString allPems;
|
||||
|
||||
void InitPem()
|
||||
{
|
||||
auto die = [](ByteString message) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "AndroidCertProvider", "%s", ("failed to enumerate system certificates: " + message).c_str());
|
||||
};
|
||||
auto *env = (JNIEnv *)SDL_AndroidGetJNIEnv();
|
||||
if (!env)
|
||||
{
|
||||
return die("SDL_AndroidGetJNIEnv failed");
|
||||
}
|
||||
jclass mPowderActivity = FindClass(env, ByteString::Build(APPID, ".PowderActivity").c_str());
|
||||
if (!mPowderActivity)
|
||||
{
|
||||
return die("FindClass failed");
|
||||
}
|
||||
jmethodID midGetCertificateBundle = env->GetStaticMethodID(mPowderActivity, "getCertificateBundle", "()Ljava/lang/String;");
|
||||
if (!midGetCertificateBundle)
|
||||
{
|
||||
return die("GetStaticMethodID failed");
|
||||
}
|
||||
jstring str = (jstring)env->CallStaticObjectMethod(mPowderActivity, midGetCertificateBundle);
|
||||
if (!str)
|
||||
{
|
||||
return die("getCertificateBundle failed");
|
||||
}
|
||||
const char *utf = env->GetStringUTFChars(str, 0);
|
||||
if (utf)
|
||||
{
|
||||
allPems = utf;
|
||||
env->ReleaseStringUTFChars(str, utf);
|
||||
}
|
||||
else
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, "AndroidCertProvider", "out of memory???");
|
||||
}
|
||||
env->DeleteLocalRef(str);
|
||||
__android_log_print(ANDROID_LOG_ERROR, "AndroidCertProvider", "certificate bundle loaded");
|
||||
}
|
||||
ByteString certificateBundle;
|
||||
|
||||
DoOnce()
|
||||
{
|
||||
InitPem();
|
||||
auto certificateBundleOpt = Platform::CallActivityStringFunc("getCertificateBundle");
|
||||
if (certificateBundleOpt)
|
||||
{
|
||||
certificateBundle = *certificateBundleOpt;
|
||||
__android_log_print(ANDROID_LOG_ERROR, APPID, "certificate bundle loaded");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static DoOnce doOnce;
|
||||
if (doOnce.allPems.size())
|
||||
if (doOnce.certificateBundle.size())
|
||||
{
|
||||
curl_blob blob;
|
||||
blob.data = doOnce.allPems.data();
|
||||
blob.len = doOnce.allPems.size();
|
||||
blob.data = doOnce.certificateBundle.data();
|
||||
blob.len = doOnce.certificateBundle.size();
|
||||
blob.flags = CURL_BLOB_COPY;
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_CAINFO_BLOB, &blob));
|
||||
}
|
||||
|
@@ -1,5 +1,11 @@
|
||||
#include "Platform.h"
|
||||
#include "Android.h"
|
||||
#include "common/Defer.h"
|
||||
#include "Config.h"
|
||||
#include <ctime>
|
||||
#include <SDL.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
@@ -28,4 +34,58 @@ bool CanUpdate()
|
||||
void SetupCrt()
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<ByteString> CallActivityStringFunc(const char *funcName)
|
||||
{
|
||||
ByteString result;
|
||||
struct CheckFailed : public std::runtime_error
|
||||
{
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
try
|
||||
{
|
||||
auto CHECK = [](auto thing, const char *what) {
|
||||
if (!thing)
|
||||
{
|
||||
throw CheckFailed(what);
|
||||
}
|
||||
return thing;
|
||||
};
|
||||
#define CHECK(a) CHECK(a, #a)
|
||||
auto *env = CHECK((JNIEnv *)SDL_AndroidGetJNIEnv());
|
||||
auto activityInst = CHECK((jobject)SDL_AndroidGetActivity());
|
||||
auto activityCls = CHECK(env->GetObjectClass(activityInst));
|
||||
auto getClassLoaderMth = CHECK(env->GetMethodID(activityCls, "getClassLoader", "()Ljava/lang/ClassLoader;"));
|
||||
auto classLoaderInst = CHECK(env->CallObjectMethod(activityInst, getClassLoaderMth));
|
||||
auto classLoaderCls = CHECK(env->FindClass("java/lang/ClassLoader"));
|
||||
auto findClassMth = CHECK(env->GetMethodID(classLoaderCls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"));
|
||||
auto strClassName = CHECK(env->NewStringUTF(ByteString::Build(APPID, ".PowderActivity").c_str()));
|
||||
Defer deleteStrClassName([env, strClassName]() { env->DeleteLocalRef(strClassName); });
|
||||
auto mPowderActivity = CHECK((jclass)(env->CallObjectMethod(classLoaderInst, findClassMth, strClassName)));
|
||||
auto funcMth = CHECK(env->GetMethodID(mPowderActivity, funcName, "()Ljava/lang/String;"));
|
||||
auto resultRef = CHECK((jstring)env->CallObjectMethod(activityInst, funcMth));
|
||||
Defer deleteStr([env, resultRef]() { env->DeleteLocalRef(resultRef); });
|
||||
auto *resultBytes = CHECK(env->GetStringUTFChars(resultRef, 0));
|
||||
Defer deleteUtf([env, resultRef, resultBytes]() { env->ReleaseStringUTFChars(resultRef, resultBytes); });
|
||||
result = resultBytes;
|
||||
}
|
||||
catch (const CheckFailed &ex)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, APPID, "CallActivityStringFunc/%s failed: %s", funcName, ex.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
#undef CHECK
|
||||
return result;
|
||||
}
|
||||
|
||||
ByteString DefaultDdir()
|
||||
{
|
||||
auto result = CallActivityStringFunc("getDefaultDdir");
|
||||
if (result)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, APPID, "DefaultDdir succeeded, data dir is %s", result->c_str());
|
||||
return *result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
8
src/common/platform/Android.h
Normal file
8
src/common/platform/Android.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <optional>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
std::optional<ByteString> CallActivityStringFunc(const char *funcName);
|
||||
}
|
@@ -42,7 +42,6 @@ elif host_platform == 'android'
|
||||
)
|
||||
powder_files += files(
|
||||
'MainCommon.cpp',
|
||||
'DdirCommon.cpp',
|
||||
)
|
||||
elif host_platform == 'emscripten'
|
||||
use_bluescreen = false
|
||||
|
Reference in New Issue
Block a user