Android can now share multiple files at once

This commit is contained in:
Yousef Khadadeh 2026-05-31 23:00:12 +03:00
parent 7f94f65bea
commit 6ef2b88d41
7 changed files with 109 additions and 78 deletions

View file

@ -28,6 +28,23 @@
#include <modules/skunicode/include/SkUnicode_icu.h>
#include "NetThreadManager.hpp"
template <typename T> class JNILocalRef {
public:
JNILocalRef(JNIEnv* e, T r):
env(e),
ref(r) {}
~JNILocalRef() {
env->DeleteLocalRef(ref);
}
T get() const { return ref; }
operator T() const { return ref; }
JNILocalRef(const JNILocalRef<T>&) = delete;
JNILocalRef& operator=(const JNILocalRef<T>&) = delete;
private:
JNIEnv* env;
T ref;
};
std::string jstring2string(JNIEnv *env, jstring jStr) {
if (!jStr)
return "";
@ -42,9 +59,9 @@ std::string jstring2string(JNIEnv *env, jstring jStr) {
return std::string(str.c_str(), str.size());
}
jstring string2jstring(JNIEnv* env, const std::string& s) {
JNILocalRef<jstring> string2jstring(JNIEnv* env, const std::string& s) {
std::u16string u16str = SkUnicodes::ICU::Make()->convertUtf8ToUtf16(s.c_str(), s.size());
return env->NewString((const jchar*)u16str.data(), u16str.length());
return JNILocalRef<jstring>(env, env->NewString((const jchar*)u16str.data(), u16str.length()));
}
namespace AndroidJNICalls {
@ -58,26 +75,31 @@ namespace AndroidJNICalls {
bool textChanged = false;
bool cursorChanged = false;
void shareInternalFile(const std::string& filePath, const std::string& mimeType) {
Logger::get().log(Logger::LogType::INFO, "[AndroidJNICalls::shareInternalFile] Share internal file " + filePath);
void shareInternalFiles(const std::vector<std::string>& filePaths, const std::string& mimeType) {
if(filePaths.empty())
return;
Logger::get().log(Logger::LogType::INFO, "[AndroidJNICalls::shareInternalFile] Share internal files");
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "shareInternalFile", "(Ljava/lang/String;Ljava/lang/String;)V");
env->CallStaticVoidMethod(clazz, method_id, string2jstring(env, filePath), string2jstring(env, mimeType));
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
JNILocalRef<jclass> jStrClass(env, env->FindClass("java/lang/String"));
JNILocalRef<jobjectArray> jFilePathArray(env, env->NewObjectArray(filePaths.size(), jStrClass, nullptr));
for(size_t i = 0; i < filePaths.size(); i++) {
JNILocalRef<jstring> s = string2jstring(env, filePaths[i]);
env->SetObjectArrayElement(jFilePathArray, i, s);
}
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "shareInternalFiles", "([Ljava/lang/String;Ljava/lang/String;)V");
env->CallStaticVoidMethod(clazz, method_id, jFilePathArray.get(), string2jstring(env, mimeType).get());
}
void shareText(const std::string& str) {
Logger::get().log(Logger::LogType::INFO, "[AndroidJNICalls::shareText] Share text " + str);
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "shareText", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(clazz, method_id, string2jstring(env, str));
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "shareText", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(clazz, method_id, string2jstring(env, str).get());
}
int get_android_text_pos_from_cursor_pos(const std::shared_ptr<RichText::TextBox>& t, const RichText::TextPosition& p) {
@ -108,36 +130,30 @@ namespace AndroidJNICalls {
modMap = nullptr;
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "startTextInput", "(JLjava/lang/String;III)V");
env->CallStaticVoidMethod(clazz, method_id, textboxID, string2jstring(env, textBox->get_string()),
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "startTextInput", "(JLjava/lang/String;III)V");
env->CallStaticVoidMethod(clazz, method_id, textboxID, string2jstring(env, textBox->get_string()).get(),
get_android_text_pos_from_cursor_pos(textBox, std::min(cursor->selectionBeginPos, cursor->selectionEndPos)),
get_android_text_pos_from_cursor_pos(textBox, std::max(cursor->selectionBeginPos, cursor->selectionEndPos)), inputType);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
void startNetworkService() {
Logger::get().log(Logger::LogType::INFO, "[AndroidJNICalls::startNetworkService] Start service");
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "startNetworkService", "()V");
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "startNetworkService", "()V");
env->CallStaticVoidMethod(clazz, method_id);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
void stopNetworkService() {
Logger::get().log(Logger::LogType::INFO, "[AndroidJNICalls::stopNetworkService] Stop service");
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "stopNetworkService", "()V");
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "stopNetworkService", "()V");
env->CallStaticVoidMethod(clazz, method_id);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
void updateModMap(CustomEvents::InputTextBoxID tId, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& newModMap) {
@ -153,14 +169,12 @@ namespace AndroidJNICalls {
*cursor = *newCursor;
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "updateCursorPos", "(II)V");
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "updateCursorPos", "(II)V");
env->CallStaticVoidMethod(clazz, method_id,
get_android_text_pos_from_cursor_pos(textBox, std::min(cursor->selectionBeginPos, cursor->selectionEndPos)),
get_android_text_pos_from_cursor_pos(textBox, std::max(cursor->selectionBeginPos, cursor->selectionEndPos)));
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
}
@ -172,14 +186,12 @@ namespace AndroidJNICalls {
textBox->set_rich_text_data(newTextbox->get_rich_text_data());
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
jobject activity = (jobject)SDL_GetAndroidActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID method_id = env->GetStaticMethodID(clazz, "updateCursorPos", "(II)V");
JNILocalRef<jobject> activity(env, (jobject)SDL_GetAndroidActivity());
JNILocalRef<jclass> clazz(env, env->GetObjectClass(activity));
jmethodID method_id = env->GetStaticMethodID(clazz.get(), "updateCursorPos", "(II)V");
env->CallStaticVoidMethod(clazz, method_id,
get_android_text_pos_from_cursor_pos(textBox, std::min(cursor->selectionBeginPos, cursor->selectionEndPos)),
get_android_text_pos_from_cursor_pos(textBox, std::max(cursor->selectionBeginPos, cursor->selectionEndPos)));
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
}

View file

@ -26,7 +26,7 @@ struct InputManager;
namespace AndroidJNICalls {
extern InputManager* globalInputManager;
void shareInternalFile(const std::string& filePath, const std::string& mimeType);
void shareInternalFiles(const std::vector<std::string>& filePaths, const std::string& mimeType);
void shareText(const std::string& str);
void startTextInput(CustomEvents::InputTextBoxID newTextboxID, const std::shared_ptr<RichText::TextBox>& newTextbox, const std::shared_ptr<RichText::TextBox::Cursor>& newCursor, const std::shared_ptr<RichText::TextStyleModifier::ModifierMap>& modMap, int inputType);
void startNetworkService();

View file

@ -428,14 +428,15 @@ void FileSelectScreen::move_selected_files(const std::filesystem::path& fromPath
}
void FileSelectScreen::share_selected_files() {
for(const FileInfo& f : fileList) {
if(f.selected) {
#ifdef __ANDROID__
AndroidJNICalls::shareInternalFile("saves/" + f.fileName + ".infpnt", "application/octet-stream");
#endif
break;
}
std::vector<std::string> filesToSend;
for(const FileInfo& f : fileList) {
if(f.selected)
filesToSend.emplace_back("saves/" + f.fileName + ".infpnt");
}
if(!filesToSend.empty())
AndroidJNICalls::shareInternalFiles(filesToSend, "application/octet-stream");
#endif
}
void FileSelectScreen::duplicate_selected_files(const std::filesystem::path& inPath) {
@ -583,13 +584,11 @@ void FileSelectScreen::edit_action_bar() {
editMode = false;
});
#ifdef __ANDROID__
if(numberOfSelectedEntries == 1) {
edit_action_bar_button("share", "data/icons/RemixIcon/share-line.svg", "Share", [&] {
share_selected_files();
update_file_list(fileList, savePath, false);
editMode = false;
});
}
edit_action_bar_button("share", "data/icons/RemixIcon/share-line.svg", "Share", [&] {
share_selected_files();
update_file_list(fileList, savePath, false);
editMode = false;
});
#endif
}
else if(selectedMenu == SelectedMenu::TRASH) {