mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-08 22:26:51 +02:00
android native copy
This commit is contained in:
@@ -1,18 +1,27 @@
|
||||
package eu.vcmi.vcmi.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import org.libsdl.app.SDL;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.lang.Exception;
|
||||
|
||||
import eu.vcmi.vcmi.Const;
|
||||
import eu.vcmi.vcmi.Storage;
|
||||
|
||||
/**
|
||||
@@ -104,4 +113,50 @@ public class FileUtil
|
||||
target.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
|
||||
private static void copyFileFromUri(String sourceFileUri, String destinationFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
final Context ctx = SDL.getContext();
|
||||
final InputStream inputStream = new FileInputStream(ctx.getContentResolver().openFileDescriptor(Uri.parse(sourceFileUri), "r").getFileDescriptor());
|
||||
final OutputStream outputStream = new FileOutputStream(new File(destinationFile));
|
||||
|
||||
copyStream(inputStream, outputStream);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e("copyFileFromUri failed: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
|
||||
private static String getFilenameFromUri(String sourceFileUri)
|
||||
{
|
||||
try
|
||||
{
|
||||
String fileName = "";
|
||||
|
||||
final Context ctx = SDL.getContext();
|
||||
ContentResolver contentResolver = ctx.getContentResolver();
|
||||
Cursor cursor = contentResolver.query(Uri.parse(sourceFileUri), null, null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if (nameIndex != -1) {
|
||||
fileName = cursor.getString(nameIndex);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("getFilenameFromUri failed: ", e);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -369,8 +369,19 @@ void FirstLaunchView::extractGogData()
|
||||
|
||||
QString tmpFileExe = tempDir.filePath("h3_gog.exe");
|
||||
QString tmpFileBin = tempDir.filePath("h3_gog-1.bin");
|
||||
#ifdef VCMI_ANDROID
|
||||
auto copy = [](QString src, QString dst)
|
||||
{
|
||||
auto srcStr = QAndroidJniObject::fromString(src);
|
||||
auto dstStr = QAndroidJniObject::fromString(dst);
|
||||
QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/util/FileUtil", "copyFileFromUri", "(Ljava/lang/String;Ljava/lang/String;)V", srcStr.object<jstring>(), dstStr.object<jstring>());
|
||||
};
|
||||
copy(fileExe, tmpFileExe);
|
||||
copy(fileBin, tmpFileBin);
|
||||
#else
|
||||
QFile(fileExe).copy(tmpFileExe);
|
||||
QFile(fileBin).copy(tmpFileBin);
|
||||
#endif
|
||||
|
||||
logGlobal->info("Installing exe '%s' ('%s')", tmpFileExe.toStdString(), fileExe.toStdString());
|
||||
logGlobal->info("Installing bin '%s' ('%s')", tmpFileBin.toStdString(), fileBin.toStdString());
|
||||
@@ -414,9 +425,13 @@ void FirstLaunchView::extractGogData()
|
||||
{
|
||||
if(!errorText.isEmpty())
|
||||
{
|
||||
logGlobal->error("Gog installer extraction failure! Reason: %s", errorText.toStdString());
|
||||
QMessageBox::critical(this, tr("Extracting error!"), errorText, QMessageBox::Ok, QMessageBox::Ok);
|
||||
if(!hashError.isEmpty())
|
||||
{
|
||||
logGlobal->error("Hash error: %s", hashError.toStdString());
|
||||
QMessageBox::critical(this, tr("Hash error!"), hashError, QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, tr("No Heroes III data!"), tr("Selected files do not contain Heroes III data!"), QMessageBox::Ok, QMessageBox::Ok);
|
||||
@@ -641,4 +656,3 @@ void FirstLaunchView::on_pushButtonGithub_clicked()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://github.com/vcmi/vcmi"));
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,11 @@
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
@@ -264,11 +269,25 @@ void MainWindow::dropEvent(QDropEvent* event)
|
||||
|
||||
void MainWindow::manualInstallFile(QString filePath)
|
||||
{
|
||||
if(filePath.endsWith(".zip", Qt::CaseInsensitive) || filePath.endsWith(".exe", Qt::CaseInsensitive))
|
||||
#ifdef VCMI_ANDROID
|
||||
QString realFilePath{};
|
||||
if(filePath.contains("content://", Qt::CaseInsensitive))
|
||||
{
|
||||
auto path = QAndroidJniObject::fromString(filePath);
|
||||
realFilePath = QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/util/FileUtil", "getFilenameFromUri", "(Ljava/lang/String;)Ljava/lang/String;", path.object<jstring>()).toString();
|
||||
}
|
||||
else
|
||||
realFilePath = filePath;
|
||||
|
||||
#else
|
||||
QString realFilePath = filePath;
|
||||
#endif
|
||||
|
||||
if(realFilePath.endsWith(".zip", Qt::CaseInsensitive) || realFilePath.endsWith(".exe", Qt::CaseInsensitive))
|
||||
switchToModsTab();
|
||||
|
||||
QString fileName = QFileInfo{filePath}.fileName();
|
||||
if(filePath.endsWith(".zip", Qt::CaseInsensitive))
|
||||
if(realFilePath.endsWith(".zip", Qt::CaseInsensitive))
|
||||
{
|
||||
QString filenameClean = fileName.toLower()
|
||||
// mod name currently comes from zip file -> remove suffixes from github zip download
|
||||
@@ -278,7 +297,7 @@ void MainWindow::manualInstallFile(QString filePath)
|
||||
|
||||
getModView()->downloadFile(filenameClean, QUrl::fromLocalFile(filePath), "mods");
|
||||
}
|
||||
else if(filePath.endsWith(".json", Qt::CaseInsensitive))
|
||||
else if(realFilePath.endsWith(".json", Qt::CaseInsensitive))
|
||||
{
|
||||
QDir configDir(QString::fromStdString(VCMIDirs::get().userConfigPath().string()));
|
||||
QStringList configFile = configDir.entryList({fileName}, QDir::Filter::Files); // case insensitive check
|
||||
|
@@ -16,6 +16,11 @@
|
||||
|
||||
#include "../innoextract.h"
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
|
||||
ChroniclesExtractor::ChroniclesExtractor(QWidget *p, std::function<void(float percent)> cb) :
|
||||
parent(p), cb(cb)
|
||||
{
|
||||
@@ -72,10 +77,15 @@ bool ChroniclesExtractor::extractGogInstaller(QString file)
|
||||
|
||||
if(!errorText.isEmpty())
|
||||
{
|
||||
logGlobal->error("Gog chronicles installer extraction failure! Reason: %s", errorText.toStdString());
|
||||
|
||||
QString hashError = Innoextract::getHashError(file, {}, {}, {});
|
||||
QMessageBox::critical(parent, tr("Extracting error!"), errorText);
|
||||
if(!hashError.isEmpty())
|
||||
{
|
||||
logGlobal->error("Hash error: %s", hashError.toStdString());
|
||||
QMessageBox::critical(parent, tr("Hash error!"), hashError, QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -226,14 +236,22 @@ void ChroniclesExtractor::installChronicles(QStringList exe)
|
||||
if(!createTempDir())
|
||||
continue;
|
||||
|
||||
logGlobal->info("Copying offline installer");
|
||||
// FIXME: this is required at the moment for Android (and possibly iOS)
|
||||
// Incoming file names are in content URI form, e.g. content://media/internal/chronicles.exe
|
||||
// Qt can handle those like it does regular files
|
||||
// however, innoextract fails to open such files
|
||||
// so make a copy in directory to which vcmi always has full access and operate on it
|
||||
QString filepath = tempDir.filePath("chr.exe");
|
||||
logGlobal->info("Copying offline installer from '%s' to '%s'", f.toStdString(), filepath.toStdString());
|
||||
#ifdef VCMI_ANDROID
|
||||
{
|
||||
auto srcStr = QAndroidJniObject::fromString(f);
|
||||
auto dstStr = QAndroidJniObject::fromString(filepath);
|
||||
QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/util/FileUtil", "copyFileFromUri", "(Ljava/lang/String;Ljava/lang/String;)V", srcStr.object<jstring>(), dstStr.object<jstring>());
|
||||
};
|
||||
#else
|
||||
QFile(f).copy(filepath);
|
||||
#endif
|
||||
QFile file(filepath);
|
||||
|
||||
logGlobal->info("Extracting offline installer");
|
||||
|
@@ -17,6 +17,11 @@
|
||||
#include <QCryptographicHash>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
|
||||
#include "modstatemodel.h"
|
||||
#include "modstateitemmodel_moc.h"
|
||||
#include "modstatecontroller.h"
|
||||
@@ -739,13 +744,25 @@ void CModListView::installFiles(QStringList files)
|
||||
// TODO: some better way to separate zip's with mods and downloaded repository files
|
||||
for(QString filename : files)
|
||||
{
|
||||
if(filename.endsWith(".zip", Qt::CaseInsensitive))
|
||||
#ifdef VCMI_ANDROID
|
||||
QString realFilename{};
|
||||
if(filename.contains("content://", Qt::CaseInsensitive))
|
||||
{
|
||||
auto path = QAndroidJniObject::fromString(filename);
|
||||
realFilename = QAndroidJniObject::callStaticObjectMethod("eu/vcmi/vcmi/util/FileUtil", "getFilenameFromUri", "(Ljava/lang/String;)Ljava/lang/String;", path.object<jstring>()).toString();
|
||||
}
|
||||
else
|
||||
realFilename = filename;
|
||||
#else
|
||||
QString realFilename = filename;
|
||||
#endif
|
||||
if(realFilename.endsWith(".zip", Qt::CaseInsensitive))
|
||||
mods.push_back(filename);
|
||||
else if(filename.endsWith(".h3m", Qt::CaseInsensitive) || filename.endsWith(".h3c", Qt::CaseInsensitive) || filename.endsWith(".vmap", Qt::CaseInsensitive) || filename.endsWith(".vcmp", Qt::CaseInsensitive))
|
||||
else if(realFilename.endsWith(".h3m", Qt::CaseInsensitive) || realFilename.endsWith(".h3c", Qt::CaseInsensitive) || realFilename.endsWith(".vmap", Qt::CaseInsensitive) || realFilename.endsWith(".vcmp", Qt::CaseInsensitive))
|
||||
maps.push_back(filename);
|
||||
if(filename.endsWith(".exe", Qt::CaseInsensitive))
|
||||
if(realFilename.endsWith(".exe", Qt::CaseInsensitive))
|
||||
exe.push_back(filename);
|
||||
else if(filename.endsWith(".json", Qt::CaseInsensitive))
|
||||
else if(realFilename.endsWith(".json", Qt::CaseInsensitive))
|
||||
{
|
||||
//download and merge additional files
|
||||
JsonNode repoData = JsonUtils::jsonFromFile(filename);
|
||||
@@ -773,7 +790,7 @@ void CModListView::installFiles(QStringList files)
|
||||
JsonUtils::merge(accumulatedRepositoryData[modNameLower], repoData);
|
||||
}
|
||||
}
|
||||
else if(filename.endsWith(".png", Qt::CaseInsensitive))
|
||||
else if(realFilename.endsWith(".png", Qt::CaseInsensitive))
|
||||
images.push_back(filename);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user