279 lines
10 KiB
C++
279 lines
10 KiB
C++
/*
|
|
Copyright (C) 2024 Rohith Namboothiri
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "LogHandler.h"
|
|
#include <QDir>
|
|
#include <QDebug>
|
|
#include <QTextStream>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
#include <QStandardPaths>
|
|
#include <QFileInfo>
|
|
|
|
#ifdef Q_OS_ANDROID
|
|
#include <QtCore/QJniObject>
|
|
#include <QtCore/QCoreApplication>
|
|
#endif
|
|
|
|
LogHandler::LogHandler(QObject *parent) : QObject(parent)
|
|
{
|
|
}
|
|
|
|
QString LogHandler::getFilePath(const QString &fileName) const
|
|
{
|
|
QString dirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
QDir dir(dirPath);
|
|
|
|
if (!dir.exists()) {
|
|
if (!dir.mkpath(dirPath)) {
|
|
qDebug() << "Failed to create directory:" << dirPath;
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
QString filePath = dirPath + "/" + fileName;
|
|
qDebug() << "Log file path:" << filePath; // Log the file path
|
|
return filePath;
|
|
}
|
|
|
|
bool LogHandler::saveLog(const QString &fileName, const QJsonArray &logData)
|
|
{
|
|
QString filePath = getFilePath(fileName);
|
|
if (filePath.isEmpty()) {
|
|
return false; // Failed to create directory, so return false
|
|
}
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
qDebug() << "Failed to open file for writing:" << file.errorString();
|
|
return false;
|
|
}
|
|
QJsonDocument doc(logData);
|
|
file.write(doc.toJson());
|
|
file.close();
|
|
qDebug() << "Log saved successfully.";
|
|
return true;
|
|
}
|
|
|
|
QJsonArray LogHandler::loadLog(const QString &fileName)
|
|
{
|
|
QString filePath = getFilePath(fileName);
|
|
QJsonArray logData;
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
qDebug() << "Failed to open file for reading:" << file.errorString();
|
|
return logData; // Return empty array if the file can't be opened
|
|
}
|
|
|
|
QByteArray data = file.readAll();
|
|
QJsonDocument doc(QJsonDocument::fromJson(data));
|
|
if (!doc.isNull() && doc.isArray()) {
|
|
logData = doc.array();
|
|
qDebug() << "Log loaded successfully.";
|
|
} else {
|
|
qDebug() << "Failed to parse JSON log.";
|
|
}
|
|
|
|
file.close();
|
|
return logData;
|
|
}
|
|
|
|
bool LogHandler::clearLog(const QString &fileName)
|
|
{
|
|
QString filePath = getFilePath(fileName);
|
|
QFile file(filePath);
|
|
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
|
file.close();
|
|
qDebug() << "Log cleared successfully.";
|
|
return true;
|
|
}
|
|
qDebug() << "Failed to clear log:" << file.errorString();
|
|
return false;
|
|
}
|
|
|
|
QString LogHandler::getDSLogPath() const {
|
|
QString externalStoragePath = "/storage/emulated/0/Download";
|
|
QString dsLogPath = externalStoragePath + "/DSLog";
|
|
QDir dsLogDir(dsLogPath);
|
|
|
|
qDebug() << "External storage path: " << externalStoragePath; // Debugging line
|
|
qDebug() << "DSLog path: " << dsLogPath; // Debugging line
|
|
|
|
if (!dsLogDir.exists()) {
|
|
if (!dsLogDir.mkpath(dsLogPath)) {
|
|
qDebug() << "Failed to create DSLog directory.";
|
|
return QString();
|
|
} else {
|
|
qDebug() << "DSLog directory created successfully.";
|
|
}
|
|
} else {
|
|
qDebug() << "DSLog directory already exists.";
|
|
}
|
|
|
|
return dsLogPath;
|
|
}
|
|
|
|
bool LogHandler::exportLogToCsv(const QString &fileName, const QJsonArray &logData) {
|
|
QString dsLogPath = getDSLogPath();
|
|
if (dsLogPath.isEmpty()) {
|
|
qDebug() << "DSLog path is not available.";
|
|
return false;
|
|
}
|
|
|
|
// Ensure that only the filename is appended to the directory path
|
|
QString filePath = dsLogPath + "/" + QFileInfo(fileName).fileName();
|
|
|
|
qDebug() << "Attempting to save file at: " << filePath;
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
qDebug() << "Failed to open file for writing:" << file.errorString();
|
|
return false;
|
|
}
|
|
|
|
QTextStream out(&file);
|
|
|
|
// Write the CSV headers
|
|
out << "Sr.No,Callsign,DMR ID,TGID,Handle,Country,Time\n";
|
|
|
|
// Write the log data to the CSV file
|
|
for (int i = 0; i < logData.size(); ++i) {
|
|
QJsonObject entry = logData[i].toObject();
|
|
out << entry["serialNumber"].toInt() << ","
|
|
<< entry["callsign"].toString() << ","
|
|
<< entry["dmrID"].toInt() << ","
|
|
<< entry["tgid"].toInt() << ","
|
|
<< entry["fname"].toString() << ","
|
|
<< entry["country"].toString() << ","
|
|
<< entry["currentTime"].toString() << "\n";
|
|
}
|
|
|
|
file.close();
|
|
qDebug() << "Log exported successfully to" << filePath;
|
|
return true;
|
|
}
|
|
|
|
bool LogHandler::exportLogToAdif(const QString &fileName, const QJsonArray &logData) {
|
|
QString dsLogPath = getDSLogPath();
|
|
if (dsLogPath.isEmpty()) {
|
|
qDebug() << "DSLog path is not available.";
|
|
return false;
|
|
}
|
|
|
|
QString filePath = dsLogPath + "/" + QFileInfo(fileName).fileName(); // Ensure the file name is properly handled
|
|
|
|
qDebug() << "Attempting to save ADIF file at: " << filePath;
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
qDebug() << "Failed to open file for writing:" << file.errorString();
|
|
return false;
|
|
}
|
|
|
|
QTextStream out(&file);
|
|
|
|
// Write the ADIF headers
|
|
out << "ADIF Export\n";
|
|
out << "<EOH>\n"; // End of Header
|
|
|
|
// Write each QSO record in ADIF format
|
|
for (int i = 0; i < logData.size(); ++i) {
|
|
QJsonObject entry = logData[i].toObject();
|
|
|
|
// Extract and format date and time
|
|
QString currentTime = entry["currentTime"].toString();
|
|
QString qsoDate = currentTime.left(10).remove('-'); // Format: YYYYMMDD
|
|
QString timeOn = currentTime.mid(11, 8).remove(':'); // Format: HHMMSS
|
|
|
|
// Write each QSO record with valid ADIF tags
|
|
out << "<CALL:" << entry["callsign"].toString().length() << ">" << entry["callsign"].toString();
|
|
out << "<BAND:4>70CM"; // Band is hardcoded as "70CM"
|
|
out << "<MODE:12>DIGITALVOICE"; // Mode is set to "DIGITALVOICE"
|
|
// Include the first name in the ADIF record
|
|
out << "<NAME:" << entry["fname"].toString().length() << ">" << entry["fname"].toString();
|
|
out << "<QSO_DATE:" << qsoDate.length() << ">" << qsoDate;
|
|
out << "<TIME_ON:6>" << timeOn;
|
|
out << "<EOR>\n"; // End of Record
|
|
}
|
|
|
|
file.close();
|
|
qDebug() << "Log exported successfully to" << filePath;
|
|
return true;
|
|
}
|
|
|
|
|
|
// Extract and display a user-friendly path
|
|
QString LogHandler::getFriendlyPath(const QString &fullPath) const {
|
|
return fullPath.mid(fullPath.indexOf("/Download/"));
|
|
}
|
|
|
|
void LogHandler::shareFile(const QString &filePath) {
|
|
#ifdef Q_OS_IOS
|
|
shareFileOnIOS(filePath); // iOS-specific sharing method
|
|
#elif defined(Q_OS_ANDROID)
|
|
shareFileDirectly(filePath); // Android-specific sharing method
|
|
#else
|
|
qWarning("File sharing is only implemented for iOS and Android.");
|
|
#endif
|
|
}
|
|
void LogHandler::shareFileDirectly(const QString &filePath) {
|
|
QJniObject context = QNativeInterface::QAndroidApplication::context();
|
|
|
|
if (context.isValid()) {
|
|
QString relativeFilePath = filePath.section("Download/", 1);
|
|
QJniObject javaFile("java/io/File", "(Ljava/lang/String;)V", QJniObject::fromString("/storage/emulated/0/Download/" + relativeFilePath).object<jstring>());
|
|
|
|
if (javaFile.isValid()) {
|
|
QString authority = "com.dmr.droidstardmr.fileprovider";
|
|
QJniObject authorityObject = QJniObject::fromString(authority);
|
|
QJniObject fileUri = QJniObject::callStaticObjectMethod(
|
|
"androidx/core/content/FileProvider",
|
|
"getUriForFile",
|
|
"(Landroid/content/Context;Ljava/lang/String;Ljava/io/File;)Landroid/net/Uri;",
|
|
context.object(),
|
|
authorityObject.object<jstring>(),
|
|
javaFile.object()
|
|
);
|
|
|
|
if (fileUri.isValid()) {
|
|
QJniObject shareIntent("android/content/Intent", "(Ljava/lang/String;)V", QJniObject::fromString("android.intent.action.SEND").object());
|
|
shareIntent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QJniObject::fromString("text/csv").object());
|
|
shareIntent.callObjectMethod("putExtra", "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;", QJniObject::fromString("android.intent.extra.STREAM").object(), fileUri.object());
|
|
|
|
shareIntent.callMethod<void>("addFlags", "(I)V", jint(1));
|
|
|
|
context.callObjectMethod("startActivity", "(Landroid/content/Intent;)V", QJniObject::callStaticObjectMethod(
|
|
"android/content/Intent", "createChooser",
|
|
"(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;",
|
|
shareIntent.object(),
|
|
QJniObject::fromString("Share File").object()
|
|
).object());
|
|
} else {
|
|
qWarning("Failed to obtain file URI for sharing.");
|
|
}
|
|
} else {
|
|
qWarning("Failed to create java.io.File object.");
|
|
}
|
|
} else {
|
|
qWarning("Invalid context or file path object.");
|
|
}
|
|
}
|
|
|