Import infpnt files to android version
This commit is contained in:
parent
cde106eba7
commit
0da317e87c
17 changed files with 135 additions and 9 deletions
1
android-project/app/proguard-rules.pro
vendored
1
android-project/app/proguard-rules.pro
vendored
|
|
@ -24,6 +24,7 @@
|
|||
void stopNetworkService();
|
||||
void shareText(java.lang.String);
|
||||
void shareInternalFiles(java.lang.String[], java.lang.String);
|
||||
java.lang.String getFileNameFromUriString(java.lang.String);
|
||||
}
|
||||
|
||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@
|
|||
<meta-data android:name="SDL_ENV.SDL_ANDROID_TRAP_BACK_BUTTON" android:value="0"/>
|
||||
-->
|
||||
<service
|
||||
android:name="InfiniPaintNetworkService"
|
||||
android:name=".InfiniPaintNetworkService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="false">
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
|
|
@ -103,11 +103,12 @@
|
|||
android:label="@string/app_name"
|
||||
android:windowSoftInputMode="adjustNothing"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
android:exported="true"
|
||||
>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
|
|||
|
|
@ -22,10 +22,12 @@ import static androidx.core.content.FileProvider.getUriForFile;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -202,4 +204,35 @@ public class InfiniPaint extends SDLActivity {
|
|||
Intent shareIntent = Intent.createChooser(sendIntent, null);
|
||||
getContext().startActivity(shareIntent);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/25005243
|
||||
static public String getFileNameFromUriString(String strUri) {
|
||||
if(strUri == null)
|
||||
return null;
|
||||
Uri uri = Uri.parse(strUri);
|
||||
if(uri == null)
|
||||
return null;
|
||||
String result = null;
|
||||
if (uri.getScheme() != null && uri.getScheme().equals("content")) {
|
||||
Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
result = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
|
||||
}
|
||||
} finally {
|
||||
if(cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = uri.getPath();
|
||||
if(result == null)
|
||||
return null;
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1) {
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,3 +189,16 @@ std::vector<std::string> glob_path_as_string_list(const std::filesystem::path& f
|
|||
toRet[i] = pathToStr(std::filesystem::path(filesInPath[i]));
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::filesystem::path sdl_safe_copy_file(const std::filesystem::path& toPath, const std::filesystem::path& fileToCopy, const std::string& fileName, const std::string& fileExtension) {
|
||||
std::vector<std::string> toFolderListNames;
|
||||
try {
|
||||
toFolderListNames = glob_path_as_string_list(toPath, "*", 0, [&](const auto& p){ return p.stem().string();});
|
||||
} catch(...) {
|
||||
SDL_CreateDirectory(toPath.string().c_str());
|
||||
}
|
||||
std::string newFileName = ensure_string_unique(toFolderListNames, std::filesystem::path(fileName).stem().string());
|
||||
std::filesystem::path newPath = toPath / (newFileName + "." + fileExtension);
|
||||
SDL_CopyFile(fileToCopy.string().c_str(), newPath.string().c_str());
|
||||
return newPath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,3 +36,4 @@ std::string remove_carriage_returns_from_str(std::string s);
|
|||
std::string ensure_string_unique(const std::vector<std::string>& stringList, std::string str);
|
||||
std::string sdl_time_to_nice_access_time(const SDL_DateTime& t, SDL_DateFormat dF, SDL_TimeFormat tF);
|
||||
std::vector<std::string> glob_path_as_string_list(const std::filesystem::path& folder, const char* globStr, SDL_GlobFlags globFlags, const std::function<std::string(const std::filesystem::path)>& pathToStr);
|
||||
std::filesystem::path sdl_safe_copy_file(const std::filesystem::path& toPath, const std::filesystem::path& fileToCopy, const std::string& fileName, const std::string& fileExtension);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,22 @@ namespace AndroidJNICalls {
|
|||
bool textChanged = false;
|
||||
bool cursorChanged = false;
|
||||
|
||||
std::string getFileNameFromURI(const std::string& uri) {
|
||||
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
|
||||
std::string strToRet = "";
|
||||
jni_local_frame(env, 16, [&] {
|
||||
jobject activity = (jobject) SDL_GetAndroidActivity();
|
||||
jclass clazz = env->GetObjectClass(activity);
|
||||
jmethodID method_id = env->GetStaticMethodID(clazz, "getFileNameFromUriString",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
jobject obj = env->CallStaticObjectMethod(clazz, method_id, string2jstring(env, uri));
|
||||
if(obj == nullptr)
|
||||
return;
|
||||
strToRet = jstring2string(env, static_cast<jstring>(obj));
|
||||
});
|
||||
return strToRet;
|
||||
}
|
||||
|
||||
void shareInternalFiles(const std::vector<std::string>& filePaths, const std::string& mimeType) {
|
||||
if(filePaths.empty())
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct InputManager;
|
|||
namespace AndroidJNICalls {
|
||||
extern InputManager* globalInputManager;
|
||||
|
||||
std::string getFileNameFromURI(const std::string& uri);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ void init() {
|
|||
AddFileToCanvasEvent::EVENT_NUM = SDL_RegisterEvents(1);
|
||||
RefreshTextBoxInputEvent::EVENT_NUM = SDL_RegisterEvents(1);
|
||||
AndroidTextBoxInputEvent::EVENT_NUM = SDL_RegisterEvents(1);
|
||||
MobileImportCanvasEvent::EVENT_NUM = SDL_RegisterEvents(1);
|
||||
alreadyInitialized = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -50,5 +51,6 @@ uint32_t OpenInfiniPaintFileEvent::EVENT_NUM;
|
|||
uint32_t AddFileToCanvasEvent::EVENT_NUM;
|
||||
uint32_t RefreshTextBoxInputEvent::EVENT_NUM;
|
||||
uint32_t AndroidTextBoxInputEvent::EVENT_NUM;
|
||||
uint32_t MobileImportCanvasEvent::EVENT_NUM;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ namespace CustomEvents {
|
|||
Vector3i intData;
|
||||
};
|
||||
|
||||
struct AndroidInsetsChangedEvent {
|
||||
struct MobileImportCanvasEvent {
|
||||
static uint32_t EVENT_NUM;
|
||||
SCollision::AABB<float> safeWindowRect;
|
||||
std::filesystem::path filePath;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ MainProgram::MainProgram():
|
|||
Logger::get().set_log_function(Logger::LogType::WORLDFATAL, [&](const std::string& text) {
|
||||
*logFile << "[WORLDFATAL] " << text << std::endl;
|
||||
std::cout << "[WORLDFATAL] " << text << std::endl;
|
||||
logMessages.emplace_front(UserLogMessage{text, UserLogMessage::COLOR_ERROR, UserLogMessage::DISPLAY_DESKTOP_ONLY});
|
||||
logMessages.emplace_front(UserLogMessage{text, UserLogMessage::COLOR_ERROR, UserLogMessage::DISPLAY_FOR_ALL});
|
||||
if(logMessages.size() == LOG_SIZE)
|
||||
logMessages.pop_back();
|
||||
g.gui.set_to_layout();
|
||||
|
|
@ -345,7 +345,11 @@ void MainProgram::create_new_tab(const CustomEvents::OpenInfiniPaintFileEvent& o
|
|||
newWorld = std::make_shared<World>(*this, openFile);
|
||||
}
|
||||
catch(const std::runtime_error& e) {
|
||||
#ifdef __ANDROID__
|
||||
Logger::get().log(Logger::LogType::WORLDFATAL, std::string("Failed to open canvas: ") + e.what());
|
||||
#else
|
||||
Logger::get().log(Logger::LogType::WORLDFATAL, "Failed to open canvas: " + (openFile.filePathSource.has_value() ? openFile.filePathSource.value().string() : "NO PATH") + " with error: " + e.what());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
worlds.emplace_back(newWorld);
|
||||
|
|
@ -390,6 +394,12 @@ void MainProgram::input_open_infinipaint_file_callback(const CustomEvents::OpenI
|
|||
post_callback();
|
||||
}
|
||||
|
||||
void MainProgram::input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport) {
|
||||
screen->input_mobile_import_canvas_callback(mobileImport);
|
||||
g.gui.set_to_layout();
|
||||
post_callback();
|
||||
}
|
||||
|
||||
void MainProgram::input_paste_callback(const CustomEvents::PasteEvent& paste) {
|
||||
g.input_paste_callback(paste);
|
||||
screen->input_paste_callback(paste);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ class MainProgram {
|
|||
void input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile);
|
||||
void input_paste_callback(const CustomEvents::PasteEvent& paste);
|
||||
void input_android_text_box_input_callback(const CustomEvents::AndroidTextBoxInputEvent& textboxInput);
|
||||
void input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport);
|
||||
|
||||
void input_app_about_to_go_to_background_callback();
|
||||
void input_app_about_to_go_to_foreground_callback();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <cereal/archives/portable_binary.hpp>
|
||||
#include <cereal/types/unordered_map.hpp>
|
||||
#include "MainProgram.hpp"
|
||||
#include "AndroidJNICalls.hpp"
|
||||
|
||||
ResourceManager::ResourceManager(World& initWorld):
|
||||
world(initWorld)
|
||||
|
|
@ -108,7 +109,13 @@ void ResourceManager::update() {
|
|||
NetworkingObjects::NetObjTemporaryPtr<ResourceData> ResourceManager::add_resource_file(const std::filesystem::path& filePath) {
|
||||
ResourceData resource;
|
||||
resource.data = std::make_shared<std::string>();
|
||||
#ifdef __ANDROID__
|
||||
resource.name = AndroidJNICalls::getFileNameFromURI(filePath.string());
|
||||
if(resource.name.empty())
|
||||
resource.name = "New Resource";
|
||||
#else
|
||||
resource.name = std::filesystem::path(filePath).filename().string();
|
||||
#endif
|
||||
|
||||
try {
|
||||
*resource.data = read_file_to_string(filePath);
|
||||
|
|
|
|||
|
|
@ -44,8 +44,10 @@
|
|||
using namespace GUIStuff;
|
||||
using namespace ElementHelpers;
|
||||
|
||||
#define MAIN_SAVES_FOLDER_STR "saves"
|
||||
|
||||
FileSelectScreen::FileSelectScreen(MainProgram& m): Screen(m) {
|
||||
savePath = main.conf.configPath / "saves";
|
||||
savePath = main.conf.configPath / MAIN_SAVES_FOLDER_STR;
|
||||
trashPath = main.conf.configPath / "trash";
|
||||
infoPath = main.conf.configPath / "fileSelectInfo.json";
|
||||
|
||||
|
|
@ -432,7 +434,7 @@ void FileSelectScreen::share_selected_files() {
|
|||
std::vector<std::string> filesToSend;
|
||||
for(const FileInfo& f : fileList) {
|
||||
if(f.selected)
|
||||
filesToSend.emplace_back("saves/" + f.fileName + ".infpnt");
|
||||
filesToSend.emplace_back(std::string(MAIN_SAVES_FOLDER_STR) + std::string("/") + f.fileName + ".infpnt");
|
||||
}
|
||||
if(!filesToSend.empty())
|
||||
AndroidJNICalls::shareInternalFiles(filesToSend, "application/octet-stream");
|
||||
|
|
@ -702,7 +704,10 @@ void FileSelectScreen::title_bar() {
|
|||
case MoreOptionsMenu::CLOSED:
|
||||
break;
|
||||
case MoreOptionsMenu::MAIN:
|
||||
elemCount = 3;
|
||||
if(selectedMenu == SelectedMenu::FILES)
|
||||
elemCount = 4;
|
||||
else
|
||||
elemCount = 3;
|
||||
break;
|
||||
case MoreOptionsMenu::VIEW:
|
||||
elemCount = 4;
|
||||
|
|
@ -747,6 +752,17 @@ void FileSelectScreen::title_bar() {
|
|||
});
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
text_transparent_option_button("Import Canvas", "Import Canvas (.infpnt)", [&] {
|
||||
open_file_selector("Open InfiniPaint Canvas", {{"InfiniPaint Canvas", "infpnt"}}, [&](const std::filesystem::path& p, const auto& e) {
|
||||
CustomEvents::emit_event<CustomEvents::MobileImportCanvasEvent>({
|
||||
.filePath = p,
|
||||
});
|
||||
});
|
||||
moreOptionsMenu = MoreOptionsMenu::CLOSED;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1213,7 +1229,10 @@ void FileSelectScreen::input_paste_callback(const CustomEvents::PasteEvent& past
|
|||
|
||||
void FileSelectScreen::input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile) {
|
||||
main.create_new_tab(openFile);
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<PhoneDrawingProgramScreen>(main); });
|
||||
if(main.world)
|
||||
main.set_screen([&] (std::unique_ptr<Screen>) { return std::make_unique<PhoneDrawingProgramScreen>(main); });
|
||||
else if(!openFile.isClient && openFile.filePathSource.has_value()) // Invalid file, remove it
|
||||
SDL_RemovePath(openFile.filePathSource.value().string().c_str());
|
||||
}
|
||||
|
||||
void FileSelectScreen::input_global_back_button_callback() {
|
||||
|
|
@ -1232,6 +1251,22 @@ void FileSelectScreen::input_global_back_button_callback() {
|
|||
main.g.gui.set_to_layout();
|
||||
}
|
||||
|
||||
void FileSelectScreen::input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport) {
|
||||
#ifdef __ANDROID__
|
||||
std::string name = AndroidJNICalls::getFileNameFromURI(mobileImport.filePath.string());
|
||||
if(name.empty())
|
||||
name = "New File";
|
||||
std::filesystem::path newPath = sdl_safe_copy_file(savePath, mobileImport.filePath, name, World::FILE_EXTENSION);
|
||||
#else
|
||||
std::filesystem::path newPath = sdl_safe_copy_file(savePath, mobileImport.filePath, mobileImport.filePath.stem().string(), World::FILE_EXTENSION);
|
||||
#endif
|
||||
CustomEvents::emit_event<CustomEvents::OpenInfiniPaintFileEvent>({
|
||||
.isClient = false,
|
||||
.saveThumbnail = true,
|
||||
.filePathSource = newPath
|
||||
});
|
||||
}
|
||||
|
||||
void FileSelectScreen::draw(SkCanvas* canvas) {
|
||||
canvas->clear(main.g.gui.io.theme->backColor0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class FileSelectScreen : public Screen {
|
|||
virtual void input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile) override;
|
||||
virtual void input_global_back_button_callback() override;
|
||||
virtual void input_app_about_to_go_to_background_callback() override;
|
||||
virtual void input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport) override;
|
||||
|
||||
struct TrashInfo {
|
||||
struct TrashFile {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ void Screen::gui_layout_run() {}
|
|||
bool Screen::app_close_requested() { return true; }
|
||||
void Screen::input_add_file_to_canvas_callback(const CustomEvents::AddFileToCanvasEvent& addFile) {}
|
||||
void Screen::input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile) {}
|
||||
void Screen::input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport) {}
|
||||
void Screen::input_paste_callback(const CustomEvents::PasteEvent& paste) {}
|
||||
void Screen::input_android_text_box_input_callback(const CustomEvents::AndroidTextBoxInputEvent& textboxInput) {}
|
||||
void Screen::input_global_back_button_callback() {}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class Screen {
|
|||
virtual void input_open_infinipaint_file_callback(const CustomEvents::OpenInfiniPaintFileEvent& openFile);
|
||||
virtual void input_paste_callback(const CustomEvents::PasteEvent& paste);
|
||||
virtual void input_android_text_box_input_callback(const CustomEvents::AndroidTextBoxInputEvent& textboxInput);
|
||||
virtual void input_mobile_import_canvas_callback(const CustomEvents::MobileImportCanvasEvent& mobileImport);
|
||||
virtual void input_global_back_button_callback();
|
||||
virtual void input_drop_file_callback(const InputManager::DropCallbackArgs& drop);
|
||||
virtual void input_drop_text_callback(const InputManager::DropCallbackArgs& drop);
|
||||
|
|
|
|||
|
|
@ -829,6 +829,8 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
|||
}
|
||||
else if(event->type == CustomEvents::AndroidTextBoxInputEvent::EVENT_NUM)
|
||||
mS.m->input_android_text_box_input_callback(*CustomEvents::get_event<CustomEvents::AndroidTextBoxInputEvent>());
|
||||
else if(event->type == CustomEvents::MobileImportCanvasEvent::EVENT_NUM)
|
||||
mS.m->input_mobile_import_canvas_callback(*CustomEvents::get_event<CustomEvents::MobileImportCanvasEvent>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue