1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-10-01 01:06:35 -04:00

Merge pull request #1542 from srcejon/android

MainWindow updates for Android and example configurations
This commit is contained in:
Edouard Griffiths 2022-12-21 09:24:12 +01:00 committed by GitHub
commit 53c1fdb877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1357 additions and 461 deletions

View File

@ -368,6 +368,106 @@ elseif (WIN32)
"${EXTERNAL_LIBRARY_FOLDER}/libusb/MS64/dll" "${EXTERNAL_LIBRARY_FOLDER}/libusb/MS64/dll"
"${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/bin" "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/bin"
) )
elseif(ANDROID)
set(EXTERNAL_LIBRARY_FOLDER "${CMAKE_SOURCE_DIR}/external/android")
set(FFTW3F_FOUND ON CACHE INTERNAL "")
set(FFTW3F_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/fftw-3/include" CACHE INTERNAL "")
set(FFTW3F_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/fftw-3/lib/libfftw3f.a" CACHE INTERNAL "")
set(OPUS_FOUND ON CACHE INTERNAL "")
set(OPUS_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/libopus/include" CACHE INTERNAL "")
set(OPUS_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libopus/lib/libopus.a" CACHE INTERNAL "")
set(CODEC2_FOUND ON CACHE INTERNAL "")
set(CODEC2_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/codec2/include" CACHE INTERNAL "")
set(CODEC2_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/codec2/lib/libcodec2.a" CACHE INTERNAL "")
set(APT_FOUND ON CACHE INTERNAL "")
set(APT_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/aptdec/include/apt" CACHE INTERNAL "")
set(APT_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/aptdec/lib/libaptstatic.a" CACHE INTERNAL "")
set(SGP4_FOUND ON CACHE INTERNAL "")
set(SGP4_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/sgp4/include/libsgp4" CACHE INTERNAL "")
set(SGP4_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/sgp4/lib/libsgp4.a" CACHE INTERNAL "")
set(ZLIB_FOUND ON CACHE INTERNAL "")
set(ZLIB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/zlib/include" CACHE INTERNAL "")
set(ZLIB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/zlib/lib/libz.a" CACHE INTERNAL "")
set(FAAD_FOUND ON CACHE INTERNAL "")
set(FAAD_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/faad2/include" CACHE INTERNAL "")
set(FAAD_LIBRARY "${EXTERNAL_LIBRARY_FOLDER}/faad2/lib/liblibfaadstatic.a" CACHE INTERNAL "")
set(DAB_FOUND ON CACHE INTERNAL "")
set(DAB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/libdab/include/dab_lib" CACHE INTERNAL "")
set(DAB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libdab/lib/libdab_lib.so" CACHE INTERNAL "")
set(LIBMBE_FOUND ON CACHE INTERNAL "")
set(LIBMBE_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/mbelib/include/" CACHE INTERNAL "")
set(LIBMBE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/mbelib/lib/libmbe.a" CACHE INTERNAL "")
set(LIBDSDCC_FOUND ON CACHE INTERNAL "")
set(LIBDSDCC_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/dsdcc/include/" CACHE INTERNAL "")
set(LIBDSDCC_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/dsdcc/lib/libdsdcc.so" CACHE INTERNAL "")
set(FFMPEG_FOUND ON CACHE INTERNAL "")
set(FFMEG_SKIP_CHECK ON CACHE INTERNAL "")
set(FFMPEG_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(FFMPEG_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/bin" CACHE INTERNAL "")
set(AVCODEC_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVCODEC_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavcodec.so" CACHE INTERNAL "")
set(AVFORMAT_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVFORMAT_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavformat.so" CACHE INTERNAL "")
set(AVUTIL_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVUTIL_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavutil.so" CACHE INTERNAL "")
set(SWRESAMPLE_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(SWRESAMPLE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswresample.so" CACHE INTERNAL "")
set(SWSCALE_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(SWSCALE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswscale.so" CACHE INTERNAL "")
set(LIBUSB_FOUND ON CACHE INTERNAL "")
set(LIBUSB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/libusb/include/" CACHE INTERNAL "")
set(LIBUSB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libusb1.0.so" CACHE INTERNAL "")
set(LIBAIRSPYHF_FOUND ON CACHE INTERNAL "")
set(LIBAIRSPYHF_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/airspyhf/include/" CACHE INTERNAL "")
set(LIBAIRSPYHF_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/airspyhf/lib/libairspyhf.a" CACHE INTERNAL "")
set(LIBAIRSPY_FOUND ON CACHE INTERNAL "")
set(LIBAIRSPY_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/airspy/include/" CACHE INTERNAL "")
set(LIBAIRSPY_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/airspy/lib/libairspy.a" CACHE INTERNAL "")
set(LIBRTLSDR_FOUND ON CACHE INTERNAL "")
set(LIBRTLSDR_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/librtlsdr/include/" CACHE INTERNAL "")
set(LIBRTLSDR_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/librtlsdr/lib/librtlsdr.a" CACHE INTERNAL "")
set(LIMESUITE_FOUND ON CACHE INTERNAL "")
set(LIMESUITE_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/limesuite/include/" CACHE INTERNAL "")
set(LIMESUITE_LIBRARY "${EXTERNAL_LIBRARY_FOLDER}/limesuite/lib/libLimeSuite.a" CACHE INTERNAL "")
set(ANDROID_EXTRA_LIBS
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6Charts_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6Concurrent_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6MultimediaWidgets_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6SerialPort_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6TextToSpeech_arm64-v8a.so
${EXTERNAL_LIBRARY_FOLDER}/android_openssl/latest/arm64/libssl_1_1.so
${EXTERNAL_LIBRARY_FOLDER}/android_openssl/latest/arm64/libcrypto_1_1.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavcodec.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavdevice.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavfilter.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavformat.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavutil.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswresample.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswscale.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libc++_shared.so
${EXTERNAL_LIBRARY_FOLDER}/libdab/lib/libdab_lib.so
${EXTERNAL_LIBRARY_FOLDER}/dsdcc/lib/libdsdcc.so
${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libunrooted_android.so
${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libusb1.0.so
CACHE INTERNAL ""
)
endif() endif()
# When building a bundle on MacOS, we want to keep build paths in the library # When building a bundle on MacOS, we want to keep build paths in the library
@ -440,35 +540,62 @@ endif()
# for the server we don't need OpenGL/Qt Quick components # for the server we don't need OpenGL/Qt Quick components
if (BUILD_GUI) if (BUILD_GUI)
find_package(OpenGL REQUIRED) if(ANDROID)
if (ENABLE_QT6) # WebEngine not supported on Android
find_package(Qt6 if (ENABLE_QT6)
REQUIRED COMPONENTS find_package(Qt6
OpenGL REQUIRED COMPONENTS
OpenGLWidgets OpenGL
Quick OpenGLWidgets
QuickWidgets Quick
OPTIONAL_COMPONENTS QuickWidgets
WebEngineQuick TextToSpeech
WebEngineCore Svg)
WebEngineWidgets else()
TextToSpeech) find_package(Qt5
REQUIRED COMPONENTS
OpenGL
Quick
QuickWidgets
Location
TextToSpeech
Svg
AndroidExtras)
endif()
else() else()
find_package(Qt5 find_package(OpenGL REQUIRED)
REQUIRED COMPONENTS if (ENABLE_QT6)
OpenGL find_package(Qt6
Quick REQUIRED COMPONENTS
QuickWidgets OpenGL
Location OpenGLWidgets
TextToSpeech Quick
WebEngine QuickWidgets
WebEngineCore OPTIONAL_COMPONENTS
WebEngineWidgets) WebEngineQuick
WebEngineCore
WebEngineWidgets
TextToSpeech)
else()
find_package(Qt5
REQUIRED COMPONENTS
OpenGL
Quick
QuickWidgets
Location
TextToSpeech
OPTIONAL_COMPONENTS
WebEngine
WebEngineCore
WebEngineWidgets)
endif()
endif() endif()
endif() endif()
# other requirements # other requirements
find_package(PkgConfig REQUIRED) if(NOT ANDROID)
find_package(PkgConfig REQUIRED)
endif()
if(APPLE) if(APPLE)
find_package(ICONV) find_package(ICONV)
@ -506,7 +633,9 @@ if(ENABLE_EXTERNAL_LIBRARIES)
else() else()
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
find_package(FFTW3F REQUIRED) find_package(FFTW3F REQUIRED)
find_package(LibUSB REQUIRED) # used by so many packages if(NOT ANDROID)
find_package(LibUSB REQUIRED) # used by so many packages
endif()
find_package(OpenCV OPTIONAL_COMPONENTS core highgui imgproc imgcodecs videoio) # channeltx/modatv find_package(OpenCV OPTIONAL_COMPONENTS core highgui imgproc imgcodecs videoio) # channeltx/modatv
find_package(LibSigMF) # SigMF recording files support find_package(LibSigMF) # SigMF recording files support
find_package(ZLIB) # For DAB find_package(ZLIB) # For DAB
@ -514,7 +643,7 @@ else()
find_package(Codec2) find_package(Codec2)
find_package(CM256cc) find_package(CM256cc)
find_package(LibMbe) find_package(LibMbe)
find_package(SerialDV REQUIRED) find_package(SerialDV)
find_package(LibDSDcc) find_package(LibDSDcc)
find_package(Sgp4) find_package(Sgp4)
find_package(AptDec) find_package(AptDec)
@ -608,8 +737,12 @@ include_directories(
${CMAKE_SOURCE_DIR}/sdrsrv ${CMAKE_SOURCE_DIR}/sdrsrv
${CMAKE_SOURCE_DIR}/sdrbench ${CMAKE_SOURCE_DIR}/sdrbench
${CMAKE_SOURCE_DIR}/logging ${CMAKE_SOURCE_DIR}/logging
${OPENGL_INCLUDE_DIR}
) )
if(NOT ANDROID)
include_directories(
${OPENGL_INCLUDE_DIR}
)
endif()
# Set up optional make target for developer doc # Set up optional make target for developer doc
find_package(Doxygen) find_package(Doxygen)
@ -669,20 +802,61 @@ if (BUILD_GUI)
set(sdrangel_SOURCES set(sdrangel_SOURCES
app/main.cpp app/main.cpp
sdrgui/resources/sdrangel.rc sdrgui/resources/sdrangel.rc
settings/settings.qrc
) )
add_executable(${CMAKE_PROJECT_NAME} if(ANDROID AND NOT ENABLE_QT6)
${sdrangel_SOURCES} add_library(${CMAKE_PROJECT_NAME} SHARED ${sdrangel_SOURCES})
) elseif(ANDROID)
qt_add_executable(${CMAKE_PROJECT_NAME} ${sdrangel_SOURCES})
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_SOURCE_DIR}/android/qt6)
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_PLUGINS ${CMAKE_BINARY_DIR}/lib/plugins)
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_LIBS ${ANDROID_EXTRA_LIBS})
else()
add_executable(${CMAKE_PROJECT_NAME} ${sdrangel_SOURCES})
endif()
target_link_libraries(${CMAKE_PROJECT_NAME} if(ANDROID)
${OPENGL_LIBRARIES} if(ENABLE_QT6)
Qt::Widgets target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
Qt::Multimedia ${OPENGL_LIBRARIES}
sdrbase Qt::Widgets
sdrgui Qt::Multimedia
logging Qt::Svg
) Qt::QmlModels
Qt::Quick
sdrbase
sdrgui
logging
android
log
)
else()
target_link_libraries(${CMAKE_PROJECT_NAME}
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
Qt::Svg
Qt::QmlModels
Qt::Quick
Qt::AndroidExtras
sdrbase
sdrgui
logging
android
log
)
endif()
else()
target_link_libraries(${CMAKE_PROJECT_NAME}
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
sdrbase
sdrgui
logging
)
endif()
if(WIN32) if(WIN32)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES

View File

@ -26,6 +26,9 @@
#include <QGLFormat> #include <QGLFormat>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#endif #endif
#ifdef ANDROID
#include "util/android.h"
#endif
#include "loggerwithfile.h" #include "loggerwithfile.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -49,6 +52,9 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif #endif
#ifndef ANDROID
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs); // Don't use on Android, otherwise we can't access files on internal storage
#endif
QApplication a(argc, argv); QApplication a(argc, argv);
@ -111,6 +117,20 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#endif #endif
#endif #endif
#ifdef ANDROID
// Default sized sliders can be hard to move using touch GUIs, so increase szie
// FIXME: How can we do a double border around the handle, as Fusion style seems to use?
// Dialog borders are hard to see as is (perhaps as Android doesn't have a title bar), so use same color as for MDI
qApp->setStyleSheet("QSlider {min-height: 20px; } "
"QSlider::groove:horizontal { border: 1px solid #2e2e2e; height: 1px; background: #444444; margin: 1px 0;}"
"QSlider::handle:horizontal { background: #585858; border: 1px double #676767; width: 16px; margin: -8px 0px; border-radius: 3px;}"
"QSlider::sub-page {background: #ff8c00; border: 1px solid #2e2e2e;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-top-left-radius: 5px;border-bottom-left-radius: 5px;}"
"QSlider::add-page {background: #444444; border: 1px solid #2e2e2e;border-top-right-radius: 5px;border-bottom-right-radius: 5px;border-top-left-radius: 0px;border-bottom-left-radius: 0px;}"
"QDialog { border: 1px solid #ff8c00;}"
);
#endif
MainParser parser; MainParser parser;
parser.parse(*qApp); parser.parse(*qApp);
@ -160,10 +180,18 @@ int main(int argc, char* argv[])
QSurfaceFormat::setDefaultFormat(sfc); QSurfaceFormat::setDefaultFormat(sfc);
#endif #endif
qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp); #ifdef ANDROID
qtwebapp::LoggerWithFile *logger = nullptr;
qInstallMessageHandler(Android::messageHandler);
#else
qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp);
logger->installMsgHandler(); logger->installMsgHandler();
#endif
int res = runQtApplication(argc, argv, logger); int res = runQtApplication(argc, argv, logger);
delete logger; delete logger;
qWarning("SDRangel quit."); qWarning("SDRangel quit.");
return res; return res;
} }

View File

@ -315,10 +315,11 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readBlob(60, &m_geometryBytes); d.readBlob(60, &m_geometryBytes);
d.readBool(61, &m_hidden, false); d.readBool(61, &m_hidden, false);
d.readString(62, &m_checkWXAPIKey, ""); d.readString(62, &m_checkWXAPIKey, "");
#ifdef LINUX
d.readString(63, &m_mapProvider, "mapboxgl");
#else
d.readString(63, &m_mapProvider, "osm"); d.readString(63, &m_mapProvider, "osm");
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";
}
#endif #endif
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {

View File

@ -183,10 +183,11 @@ bool MapSettings::deserialize(const QByteArray& data)
QByteArray blob; QByteArray blob;
d.readBool(1, &m_displayNames, true); d.readBool(1, &m_displayNames, true);
#ifdef LINUX
d.readString(2, &m_mapProvider, "mapboxgl");
#else
d.readString(2, &m_mapProvider, "osm"); d.readString(2, &m_mapProvider, "osm");
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";
}
#endif #endif
d.readString(3, &m_mapBoxAPIKey, ""); d.readString(3, &m_mapBoxAPIKey, "");
d.readString(4, &m_mapBoxStyles, ""); d.readString(4, &m_mapBoxStyles, "");

View File

@ -1,6 +1,7 @@
project (sdrbase) project (sdrbase)
if(WIN32) if(WIN32)
# This should probably be in ../CMakeLists.txt with the other variables like it
set(OPUS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/external/windows/libopus/include") set(OPUS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/external/windows/libopus/include")
set(OPUS_LIBRARIES "${CMAKE_SOURCE_DIR}/external/windows/libopus/lib/x64/libopus.lib") set(OPUS_LIBRARIES "${CMAKE_SOURCE_DIR}/external/windows/libopus/lib/x64/libopus.lib")
endif() endif()
@ -45,10 +46,11 @@ if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(sdrbase_LIBSIGMF_LIB ${LIBSIGMF_LIBRARIES}) set(sdrbase_LIBSIGMF_LIB ${LIBSIGMF_LIBRARIES})
endif() endif()
# serialdv now required if (LIBSERIALDV_FOUND)
add_definitions(-DDSD_USE_SERIALDV) add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR}) include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY}) set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
endif()
set(sdrbase_SOURCES set(sdrbase_SOURCES
${sdrbase_SOURCES} ${sdrbase_SOURCES}
@ -168,6 +170,7 @@ set(sdrbase_SOURCES
settings/rollupstate.cpp settings/rollupstate.cpp
util/ais.cpp util/ais.cpp
util/android.cpp
util/aviationweather.cpp util/aviationweather.cpp
util/ax25.cpp util/ax25.cpp
util/aprs.cpp util/aprs.cpp
@ -388,6 +391,7 @@ set(sdrbase_HEADERS
settings/rollupstate.h settings/rollupstate.h
util/ais.h util/ais.h
util/android.h
util/aviationweather.h util/aviationweather.h
util/ax25.h util/ax25.h
util/aprs.h util/aprs.h
@ -491,6 +495,7 @@ target_link_libraries(sdrbase
Qt::Core Qt::Core
Qt::Multimedia Qt::Multimedia
Qt::WebSockets Qt::WebSockets
Qt::Positioning
httpserver httpserver
logging logging
qrtplib qrtplib
@ -499,8 +504,15 @@ target_link_libraries(sdrbase
if (Qt6_FOUND) if (Qt6_FOUND)
target_link_libraries(sdrbase target_link_libraries(sdrbase
Qt::Core5Compat Qt::Core5Compat
Qt::CorePrivate
) )
endif() endif()
if(ANDROID AND NOT ENABLE_QT6)
target_link_libraries(sdrbase Qt::AndroidExtras)
endif()
if(ANDROID)
target_link_libraries(sdrbase log)
endif()
install(TARGETS sdrbase DESTINATION ${INSTALL_LIB_DIR}) install(TARGETS sdrbase DESTINATION ${INSTALL_LIB_DIR})

View File

@ -17,8 +17,6 @@
#include <QGlobalStatic> #include <QGlobalStatic>
#include "plugin/pluginmanager.h"
#include "deviceenumerator.h" #include "deviceenumerator.h"
Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator) Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator)
@ -246,89 +244,101 @@ bool DeviceEnumerator::isMIMOEnumerated(const QString& deviceHwId, int deviceSeq
return false; return false;
} }
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type)
{ {
m_rxEnumeration.clear(); DevicesEnumeration tempEnumeration;
PluginAPI::SamplingDeviceRegistrations& rxDeviceRegistrations = pluginManager->getSourceDeviceRegistrations(); PluginInterface::OriginDevices originDevices;
int index = 0; QStringList originDevicesHwIds;
for (int i = 0; i < rxDeviceRegistrations.count(); i++) for (int i = 0; i < deviceRegistrations.count(); i++)
{ {
qDebug("DeviceEnumerator::enumerateRxDevices: %s", qPrintable(rxDeviceRegistrations[i].m_deviceId)); qDebug("DeviceEnumerator::enumerateDevices: %s", qPrintable(deviceRegistrations[i].m_deviceId));
rxDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices); deviceRegistrations[i].m_plugin->enumOriginDevices(originDevicesHwIds, originDevices);
PluginInterface::SamplingDevices samplingDevices = rxDeviceRegistrations[i].m_plugin->enumSampleSources(m_originDevices); PluginInterface::SamplingDevices samplingDevices;
if (type == PluginInterface::SamplingDevice::StreamSingleRx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSources(originDevices);
} else if (type == PluginInterface::SamplingDevice::StreamSingleTx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSinks(originDevices);
} else {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleMIMO(originDevices);
}
for (int j = 0; j < samplingDevices.count(); j++) for (int j = 0; j < samplingDevices.count(); j++)
{ {
m_rxEnumeration.push_back( tempEnumeration.push_back(
DeviceEnumeration( DeviceEnumeration(
samplingDevices[j], samplingDevices[j],
rxDeviceRegistrations[i].m_plugin, deviceRegistrations[i].m_plugin,
index 0 // Index will be set when adding to enumeration below
) )
); );
index++;
} }
} }
// We don't remove devices that are no-longer found from the enumeration list, in case their
// index is referenced in some other object (E.g. DeviceGUI)
// Instead we mark as removed. If later re-added, then we re-use the same index
for (DevicesEnumeration::iterator it = enumeration.begin(); it != enumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = tempEnumeration.begin(); it2 != tempEnumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
found = true;
break;
}
}
if (!found) {
it->m_samplingDevice.removed = true;
}
}
// Add new entries and update existing (in case re-added or sequence number has changed)
int index = enumeration.size();
for (DevicesEnumeration::iterator it = tempEnumeration.begin(); it != tempEnumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = enumeration.begin(); it2 != enumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
it2->m_samplingDevice.removed = false;
it2->m_samplingDevice.displayedName = it->m_samplingDevice.displayedName;
found = true;
break;
}
}
if (!found)
{
it->m_index = index++;
enumeration.push_back(*it);
}
}
}
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager)
{
enumerateDevices(pluginManager->getSourceDeviceRegistrations(), m_rxEnumeration, PluginInterface::SamplingDevice::StreamSingleRx);
} }
void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager)
{ {
m_txEnumeration.clear(); enumerateDevices(pluginManager->getSinkDeviceRegistrations(), m_txEnumeration, PluginInterface::SamplingDevice::StreamSingleTx);
PluginAPI::SamplingDeviceRegistrations& txDeviceRegistrations = pluginManager->getSinkDeviceRegistrations();
int index = 0;
for (int i = 0; i < txDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateTxDevices: %s", qPrintable(txDeviceRegistrations[i].m_deviceId));
txDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = txDeviceRegistrations[i].m_plugin->enumSampleSinks(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_txEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
txDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
} }
void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager) void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager)
{ {
m_mimoEnumeration.clear(); enumerateDevices(pluginManager->getMIMODeviceRegistrations(), m_mimoEnumeration, PluginInterface::SamplingDevice::StreamMIMO);
PluginAPI::SamplingDeviceRegistrations& mimoDeviceRegistrations = pluginManager->getMIMODeviceRegistrations();
int index = 0;
for (int i = 0; i < mimoDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateMIMODevices: %s", qPrintable(mimoDeviceRegistrations[i].m_deviceId));
mimoDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = mimoDeviceRegistrations[i].m_plugin->enumSampleMIMO(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_mimoEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
mimoDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
} }
void DeviceEnumerator::listRxDeviceNames(QList<QString>& list, std::vector<int>& indexes) const void DeviceEnumerator::listRxDeviceNames(QList<QString>& list, std::vector<int>& indexes) const
{ {
for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);
@ -340,7 +350,7 @@ void DeviceEnumerator::listTxDeviceNames(QList<QString>& list, std::vector<int>&
{ {
for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);
@ -352,7 +362,7 @@ void DeviceEnumerator::listMIMODeviceNames(QList<QString>& list, std::vector<int
{ {
for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it)
{ {
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{ {
list.append(it->m_samplingDevice.displayedName); list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index); indexes.push_back(it->m_index);

View File

@ -21,6 +21,7 @@
#include <vector> #include <vector>
#include "plugin/plugininterface.h" #include "plugin/plugininterface.h"
#include "plugin/pluginmanager.h"
#include "device/deviceuserargs.h" #include "device/deviceuserargs.h"
#include "export.h" #include "export.h"
@ -85,9 +86,8 @@ private:
DevicesEnumeration m_rxEnumeration; DevicesEnumeration m_rxEnumeration;
DevicesEnumeration m_txEnumeration; DevicesEnumeration m_txEnumeration;
DevicesEnumeration m_mimoEnumeration; DevicesEnumeration m_mimoEnumeration;
PluginInterface::OriginDevices m_originDevices;
QStringList m_originDevicesHwIds;
void enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type);
PluginInterface *getRxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getRxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getTxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getTxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getMIMORegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId); PluginInterface *getMIMORegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);

View File

@ -18,6 +18,8 @@
#include <QGlobalStatic> #include <QGlobalStatic>
#include <QCoreApplication> #include <QCoreApplication>
#include <QString> #include <QString>
#include <QDebug>
#include <QGeoPositionInfoSource>
#include "loggerwithfile.h" #include "loggerwithfile.h"
#include "dsp/dsptypes.h" #include "dsp/dsptypes.h"
@ -71,6 +73,8 @@ MainCore::MainCore()
m_masterTimer.start(50); m_masterTimer.start(50);
m_startMsecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); m_startMsecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
m_masterElapsedTimer.start(); m_masterElapsedTimer.start();
// Position can take a while to determine, so we start updates at program startup
initPosition();
} }
MainCore::~MainCore() MainCore::~MainCore()
@ -335,3 +339,36 @@ void MainCore::debugMaps()
feIt.value()->getIndex(), feIt.key()->getIndexInFeatureSet(), qPrintable(feIt.key()->getURI()), qPrintable(feIt.key()->getName())); feIt.value()->getIndex(), feIt.key()->getIndexInFeatureSet(), qPrintable(feIt.key()->getURI()), qPrintable(feIt.key()->getName()));
} }
} }
void MainCore::initPosition()
{
m_positionSource = QGeoPositionInfoSource::createDefaultSource(this);
if (m_positionSource)
{
connect(m_positionSource, &QGeoPositionInfoSource::positionUpdated, this, &MainCore::positionUpdated);
m_positionSource->startUpdates();
}
else
{
qDebug() << "MainCore::initPosition: No position source.";
}
}
const QGeoPositionInfo& MainCore::getPosition() const
{
return m_position;
}
void MainCore::positionUpdated(const QGeoPositionInfo &info)
{
if (info.isValid())
{
m_position = info;
if (m_settings.getAutoUpdatePosition())
{
m_settings.setLatitude(m_position.coordinate().latitude());
m_settings.setLongitude(m_position.coordinate().longitude());
m_settings.setAltitude(m_position.coordinate().altitude());
}
}
}

View File

@ -25,6 +25,7 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QDateTime> #include <QDateTime>
#include <QObject> #include <QObject>
#include <QGeoPositionInfo>
#include "export.h" #include "export.h"
#include "settings/mainsettings.h" #include "settings/mainsettings.h"
@ -38,6 +39,7 @@ class FeatureSet;
class Feature; class Feature;
class PluginManager; class PluginManager;
class MessageQueue; class MessageQueue;
class QGeoPositionInfoSource;
namespace qtwebapp { namespace qtwebapp {
class LoggerWithFile; class LoggerWithFile;
@ -846,7 +848,7 @@ public:
qint64 getStartMsecsSinceEpoch() const { return m_startMsecsSinceEpoch; } //!< Epoch timestamp in millisecodns close to elapsed timer start qint64 getStartMsecsSinceEpoch() const { return m_startMsecsSinceEpoch; } //!< Epoch timestamp in millisecodns close to elapsed timer start
const MainSettings& getSettings() const { return m_settings; } const MainSettings& getSettings() const { return m_settings; }
MessageQueue *getMainMessageQueue() { return m_mainMessageQueue; } MessageQueue *getMainMessageQueue() { return m_mainMessageQueue; }
const PluginManager *getPluginManager() const { return m_pluginManager; } PluginManager *getPluginManager() const { return m_pluginManager; }
std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; } std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; }
std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; } std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; }
void setLoggingOptions(); void setLoggingOptions();
@ -875,6 +877,8 @@ public:
// pipes // pipes
MessagePipes& getMessagePipes() { return m_messagePipes; } MessagePipes& getMessagePipes() { return m_messagePipes; }
DataPipes& getDataPipes() { return m_dataPipes; } DataPipes& getDataPipes() { return m_dataPipes; }
// Position
const QGeoPositionInfo& getPosition() const;
friend class MainServer; friend class MainServer;
friend class MainWindow; friend class MainWindow;
@ -883,6 +887,9 @@ public:
friend class DeviceSetPresetsDialog; friend class DeviceSetPresetsDialog;
friend class ConfigurationsDialog; friend class ConfigurationsDialog;
public slots:
void positionUpdated(const QGeoPositionInfo &info);
signals: signals:
void deviceSetAdded(int index, DeviceAPI *device); void deviceSetAdded(int index, DeviceAPI *device);
void deviceChanged(int index); void deviceChanged(int index);
@ -912,7 +919,10 @@ private:
PluginManager* m_pluginManager; PluginManager* m_pluginManager;
MessagePipes m_messagePipes; MessagePipes m_messagePipes;
DataPipes m_dataPipes; DataPipes m_dataPipes;
QGeoPositionInfoSource *m_positionSource;
QGeoPositionInfo m_position;
void initPosition();
void debugMaps(); void debugMaps();
}; };

View File

@ -66,6 +66,7 @@ public:
int deviceNbItems; //!< Number of items (or streams) in the device. >1 for composite devices. int deviceNbItems; //!< Number of items (or streams) in the device. >1 for composite devices.
int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized
int claimed; //!< This is the device set index if claimed else -1 int claimed; //!< This is the device set index if claimed else -1
bool removed; //!< Set if device has been removed
SamplingDevice(const QString& _displayedName, SamplingDevice(const QString& _displayedName,
const QString& _hardwareId, const QString& _hardwareId,
@ -85,8 +86,17 @@ public:
streamType(_streamType), streamType(_streamType),
deviceNbItems(_deviceNbItems), deviceNbItems(_deviceNbItems),
deviceItemIndex(_deviceItemIndex), deviceItemIndex(_deviceItemIndex),
claimed(-1) claimed(-1),
removed(false)
{ } { }
bool operator==(const SamplingDevice& rhs) const
{
return displayedName == rhs.displayedName
&& hardwareId == rhs.hardwareId
&& id == rhs.id
&& serial == rhs.serial;
}
}; };
typedef QList<SamplingDevice> SamplingDevices; typedef QList<SamplingDevice> SamplingDevices;

View File

@ -151,6 +151,12 @@ public:
emit preferenceChanged(Preferences::Altitude); emit preferenceChanged(Preferences::Altitude);
} }
bool getAutoUpdatePosition() const { return m_preferences.getAutoUpdatePosition(); }
void setAutoUpdatePosition(bool autoUpdatePosition)
{
m_preferences.setAutoUpdatePosition(autoUpdatePosition);
}
QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); } QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel) void setConsoleMinLogLevel(const QtMsgType& minLogLevel)
{ {

View File

@ -17,6 +17,7 @@ void Preferences::resetToDefaults()
m_latitude = 49.012423; // Set an interesting location so map doesn't open up in the middle of the ocean m_latitude = 49.012423; // Set an interesting location so map doesn't open up in the middle of the ocean
m_longitude = 8.418125; m_longitude = 8.418125;
m_altitude = 0.0f; m_altitude = 0.0f;
m_autoUpdatePosition = true;
m_useLogFile = false; m_useLogFile = false;
m_logFileName = "sdrangel.log"; m_logFileName = "sdrangel.log";
m_consoleMinLogLevel = QtDebugMsg; m_consoleMinLogLevel = QtDebugMsg;
@ -41,6 +42,7 @@ QByteArray Preferences::serialize() const
s.writeFloat((int) Altitude, m_altitude); s.writeFloat((int) Altitude, m_altitude);
s.writeS32((int) SourceItemIndex, m_sourceItemIndex); s.writeS32((int) SourceItemIndex, m_sourceItemIndex);
s.writeS32((int) Multisampling, m_multisampling); s.writeS32((int) Multisampling, m_multisampling);
s.writeBool((int) AutoUpdatePosition, m_autoUpdatePosition);
return s.final(); return s.final();
} }
@ -95,9 +97,11 @@ bool Preferences::deserialize(const QByteArray& data)
} }
d.readS32((int) Multisampling, &m_multisampling, 0); d.readS32((int) Multisampling, &m_multisampling, 0);
d.readBool((int) AutoUpdatePosition, &m_autoUpdatePosition, true);
return true; return true;
} else }
else
{ {
resetToDefaults(); resetToDefaults();
return false; return false;

View File

@ -22,7 +22,8 @@ public:
StationName, StationName,
Altitude, Altitude,
SourceItemIndex, SourceItemIndex,
Multisampling Multisampling,
AutoUpdatePosition
}; };
Preferences(); Preferences();
@ -60,6 +61,9 @@ public:
float getAltitude() const { return m_altitude; } float getAltitude() const { return m_altitude; }
void setAltitude(float altitude) { m_altitude = altitude; } void setAltitude(float altitude) { m_altitude = altitude; }
bool getAutoUpdatePosition() const { return m_autoUpdatePosition; }
void setAutoUpdatePosition(bool autoUpdatePosition) { m_autoUpdatePosition = autoUpdatePosition; }
QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; } QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; } void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; }
@ -87,6 +91,7 @@ protected:
float m_latitude; //!< Position of the station float m_latitude; //!< Position of the station
float m_longitude; float m_longitude;
float m_altitude; //!< Altitude in metres float m_altitude; //!< Altitude in metres
bool m_autoUpdatePosition; //!< Automatically update position from GPS
QtMsgType m_consoleMinLogLevel; QtMsgType m_consoleMinLogLevel;
QtMsgType m_fileMinLogLevel; QtMsgType m_fileMinLogLevel;

213
sdrbase/util/android.cpp Normal file
View File

@ -0,0 +1,213 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// 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 as 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifdef ANDROID
#include <QDebug>
#include <android/log.h>
#include "android.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtCore/private/qandroidextras_p.h>
#include <QJniObject>
#include <QJniEnvironment>
void Android::sendIntent()
{
QJniObject url = QJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QJniObject intent = QJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroidPrivate::startActivity(intent, 0, [](int requestCode, int resultCode, const QJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QJniEnvironment jniEnv;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = QJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#else // QT_VERSION
#include <QtAndroid>
#include <QAndroidIntent>
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include <QAndroidActivityResultReceiver>
void Android::sendIntent()
{
QAndroidJniObject url = QAndroidJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QAndroidJniObject intent = QAndroidJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroid::startActivity(intent, 0, [](int requestCode, int resultCode, const QAndroidJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QAndroidJniEnvironment jniEnv;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QAndroidJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = QAndroidJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#endif // QT6
// Redirect qDebug/qWarning to Android log, so we can view remotely with adb
void Android::messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
QString report = msg;
if (context.file && !QString(context.file).isEmpty())
{
report += " in file ";
report += QString(context.file);
report += " line ";
report += QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty())
{
report += +" function ";
report += QString(context.function);
}
const char * const local = report.toLocal8Bit().constData();
const char * const applicationName = "sdrangel";
switch (type)
{
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG, applicationName, local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO, applicationName, local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN, applicationName, local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR, applicationName, local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL, applicationName, local);
abort();
}
}
#endif // ANDROID

44
sdrbase/util/android.h Normal file
View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// 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 as 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_ANDROID_H_
#define SDRBASE_ANDROID_H_
#ifdef ANDROID
#include <QtGlobal>
#include <QString>
#include "export.h"
// Android specific functions
class SDRBASE_API Android
{
public:
static void sendIntent();
static QStringList listUSBDeviceSerials(int vid, int pid);
static int openUSBDevice(const QString &serial);
static void closeUSBDevice(int fd);
static void moveTaskToBack();
static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
};
#endif // ANDROID
#endif // SDRBASE_ANDROID_H_

View File

@ -27,11 +27,26 @@
#include "settings/configuration.h" #include "settings/configuration.h"
ConfigurationsDialog::ConfigurationsDialog(QWidget* parent) : ConfigurationsDialog::ConfigurationsDialog(bool openOnly, QWidget* parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::ConfigurationsDialog) ui(new Ui::ConfigurationsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
if (openOnly)
{
ui->buttonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Open);
ui->configurationDelete->setVisible(false);
ui->configurationEdit->setVisible(false);
ui->configurationExport->setVisible(false);
ui->configurationImport->setVisible(false);
ui->configurationLoad->setVisible(false);
ui->configurationSave->setVisible(false);
ui->configurationUpdate->setVisible(false);
}
else
{
ui->description->setVisible(false);
}
} }
ConfigurationsDialog::~ConfigurationsDialog() ConfigurationsDialog::~ConfigurationsDialog()
@ -354,9 +369,7 @@ void ConfigurationsDialog::on_configurationExport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Configuration export files (*.cfgx)"), tr("Configuration export files (*.cfgx)"),
0, 0);
QFileDialog::DontUseNativeDialog
);
if (fileName != "") if (fileName != "")
{ {
@ -404,8 +417,7 @@ void ConfigurationsDialog::on_configurationImport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.cfgx)"), tr("Preset export files (*.cfgx)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")
@ -497,3 +509,9 @@ void ConfigurationsDialog::deleteConfigurationGroup(const QString& groupName)
} }
} }
} }
void ConfigurationsDialog::accept()
{
on_configurationLoad_clicked();
QDialog::accept();
}

View File

@ -38,7 +38,7 @@ namespace Ui {
class SDRGUI_API ConfigurationsDialog : public QDialog { class SDRGUI_API ConfigurationsDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigurationsDialog(QWidget* parent = nullptr); explicit ConfigurationsDialog(bool openOnly, QWidget* parent = nullptr);
~ConfigurationsDialog(); ~ConfigurationsDialog();
void setConfigurations(QList<Configuration*>* configurations) { m_configurations = configurations; } void setConfigurations(QList<Configuration*>* configurations) { m_configurations = configurations; }
void populateTree(); void populateTree();
@ -71,6 +71,7 @@ private slots:
void on_configurationLoad_clicked(); void on_configurationLoad_clicked();
void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column); void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column);
void accept() override;
signals: signals:
void saveConfiguration(Configuration*); void saveConfiguration(Configuration*);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>392</width> <width>410</width>
<height>414</height> <height>424</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -19,218 +19,207 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Configurations</string> <string>Configurations</string>
</property> </property>
<widget class="QDialogButtonBox" name="buttonBox"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="geometry"> <item>
<rect> <widget class="QWidget" name="widget" native="true">
<x>40</x> <layout class="QVBoxLayout" name="verticalLayout">
<y>380</y> <item>
<width>341</width> <widget class="QLabel" name="description">
<height>32</height> <property name="text">
</rect> <string>Select configuration to load:</string>
</property> </property>
<property name="orientation"> </widget>
<enum>Qt::Horizontal</enum> </item>
</property> <item>
<property name="standardButtons"> <widget class="QTreeWidget" name="configurationsTree">
<set>QDialogButtonBox::Close</set> <property name="toolTip">
</property> <string>List of configurations</string>
</widget> </property>
<widget class="QWidget" name="widget" native="true"> <property name="indentation">
<property name="geometry"> <number>5</number>
<rect> </property>
<x>0</x> <property name="allColumnsShowFocus">
<y>10</y> <bool>true</bool>
<width>392</width> </property>
<height>310</height> <property name="columnCount">
</rect> <number>1</number>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <attribute name="headerMinimumSectionSize">
<item> <number>5</number>
<widget class="QTreeWidget" name="configurationsTree"> </attribute>
<property name="toolTip"> <column>
<string>List of configurations</string> <property name="text">
</property> <string>Description</string>
<property name="indentation"> </property>
<number>5</number> </column>
</property> </widget>
<property name="allColumnsShowFocus"> </item>
<bool>true</bool> </layout>
</property> </widget>
<property name="columnCount"> </item>
<number>1</number> <item>
</property> <layout class="QHBoxLayout" name="configurationsControl">
<attribute name="headerMinimumSectionSize"> <item>
<number>5</number> <widget class="QToolButton" name="configurationSave">
</attribute> <property name="toolTip">
<column> <string>Save current workspaces configuration as new configuration</string>
<property name="text">
<string>Description</string>
</property> </property>
</column> <property name="text">
</widget> <string>...</string>
</item> </property>
</layout> <property name="icon">
</widget> <iconset resource="../resources/res.qrc">
<widget class="QWidget" name="layoutWidget"> <normaloff>:/create.png</normaloff>:/create.png</iconset>
<property name="geometry"> </property>
<rect> <property name="iconSize">
<x>0</x> <size>
<y>330</y> <width>16</width>
<width>392</width> <height>16</height>
<height>34</height> </size>
</rect> </property>
</property> </widget>
<layout class="QHBoxLayout" name="configurationsControl"> </item>
<item> <item>
<widget class="QToolButton" name="configurationSave"> <widget class="QToolButton" name="configurationUpdate">
<property name="toolTip"> <property name="toolTip">
<string>Save current workspaces configuration as new configuration</string> <string>Update selected configuration with current workspaces configuration</string>
</property> </property>
<property name="text"> <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../resources/res.qrc"> <iconset resource="../resources/res.qrc">
<normaloff>:/create.png</normaloff>:/create.png</iconset> <normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
<size> <size>
<width>16</width> <width>16</width>
<height>16</height> <height>16</height>
</size> </size>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="configurationUpdate"> <widget class="QToolButton" name="configurationEdit">
<property name="toolTip"> <property name="toolTip">
<string>Update selected configuration with current workspaces configuration</string> <string>Edit configuration details</string>
</property> </property>
<property name="text"> <property name="text">
<string>...</string> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../resources/res.qrc"> <iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset> <normaloff>:/edit.png</normaloff>:/edit.png</iconset>
</property> </property>
<property name="iconSize"> </widget>
<size> </item>
<width>16</width> <item>
<height>16</height> <widget class="QToolButton" name="configurationExport">
</size> <property name="toolTip">
</property> <string>Export current configuration to file</string>
</widget> </property>
</item> <property name="text">
<item> <string/>
<widget class="QToolButton" name="configurationEdit"> </property>
<property name="toolTip"> <property name="icon">
<string>Edit configuration details</string> <iconset resource="../resources/res.qrc">
</property> <normaloff>:/export.png</normaloff>:/export.png</iconset>
<property name="text"> </property>
<string/> </widget>
</property> </item>
<property name="icon"> <item>
<iconset resource="../resources/res.qrc"> <widget class="QToolButton" name="configurationImport">
<normaloff>:/edit.png</normaloff>:/edit.png</iconset> <property name="toolTip">
</property> <string>Import configuration from file into current group</string>
</widget> </property>
</item> <property name="text">
<item> <string/>
<widget class="QToolButton" name="configurationExport"> </property>
<property name="toolTip"> <property name="icon">
<string>Export current configuration to file</string> <iconset resource="../resources/res.qrc">
</property> <normaloff>:/import.png</normaloff>:/import.png</iconset>
<property name="text"> </property>
<string/> </widget>
</property> </item>
<property name="icon"> <item>
<iconset resource="../resources/res.qrc"> <spacer name="horizontalSpacer">
<normaloff>:/export.png</normaloff>:/export.png</iconset> <property name="orientation">
</property> <enum>Qt::Horizontal</enum>
</widget> </property>
</item> <property name="sizeHint" stdset="0">
<item> <size>
<widget class="QToolButton" name="configurationImport"> <width>40</width>
<property name="toolTip"> <height>20</height>
<string>Import configuration from file into current group</string> </size>
</property> </property>
<property name="text"> </spacer>
<string/> </item>
</property> <item>
<property name="icon"> <widget class="QToolButton" name="configurationDelete">
<iconset resource="../resources/res.qrc"> <property name="toolTip">
<normaloff>:/import.png</normaloff>:/import.png</iconset> <string>Delete selected configuration</string>
</property> </property>
</widget> <property name="text">
</item> <string>...</string>
<item> </property>
<spacer name="horizontalSpacer"> <property name="icon">
<property name="orientation"> <iconset resource="../resources/res.qrc">
<enum>Qt::Horizontal</enum> <normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="iconSize">
<size> <size>
<width>40</width> <width>16</width>
<height>20</height> <height>16</height>
</size> </size>
</property> </property>
</spacer> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="configurationDelete"> <spacer name="horizontalSpacer_2">
<property name="toolTip"> <property name="orientation">
<string>Delete selected configuration</string> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="text"> <property name="sizeHint" stdset="0">
<string>...</string> <size>
</property> <width>40</width>
<property name="icon"> <height>20</height>
<iconset resource="../resources/res.qrc"> </size>
<normaloff>:/bin.png</normaloff>:/bin.png</iconset> </property>
</property> </spacer>
<property name="iconSize"> </item>
<size> <item>
<width>16</width> <widget class="QToolButton" name="configurationLoad">
<height>16</height> <property name="toolTip">
</size> <string>Load selected configuration</string>
</property> </property>
</widget> <property name="text">
</item> <string>...</string>
<item> </property>
<spacer name="horizontalSpacer_2"> <property name="icon">
<property name="orientation"> <iconset resource="../resources/res.qrc">
<enum>Qt::Horizontal</enum> <normaloff>:/load.png</normaloff>:/load.png</iconset>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="iconSize">
<size> <size>
<width>40</width> <width>16</width>
<height>20</height> <height>16</height>
</size> </size>
</property> </property>
</spacer> </widget>
</item> </item>
<item> </layout>
<widget class="QToolButton" name="configurationLoad"> </item>
<property name="toolTip"> <item>
<string>Load selected configuration</string> <widget class="QDialogButtonBox" name="buttonBox">
</property> <property name="orientation">
<property name="text"> <enum>Qt::Horizontal</enum>
<string>...</string> </property>
</property> <property name="standardButtons">
<property name="icon"> <set>QDialogButtonBox::Close</set>
<iconset resource="../resources/res.qrc"> </property>
<normaloff>:/load.png</normaloff>:/load.png</iconset> </widget>
</property> </item>
<property name="iconSize"> </layout>
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
<resources> <resources>
<include location="../resources/res.qrc"/> <include location="../resources/res.qrc"/>

View File

@ -292,8 +292,7 @@ void DeviceSetPresetsDialog::on_presetExport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.prex)"), tr("Preset export files (*.prex)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")
@ -342,8 +341,7 @@ void DeviceSetPresetsDialog::on_presetImport_clicked()
tr("Open preset export file"), tr("Open preset export file"),
".", ".",
tr("Preset export files (*.prex)"), tr("Preset export files (*.prex)"),
0, 0
QFileDialog::DontUseNativeDialog
); );
if (fileName != "") if (fileName != "")

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>324</width> <width>324</width>
<height>229</height> <height>201</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -19,44 +19,24 @@
<property name="windowTitle"> <property name="windowTitle">
<string>My Position</string> <string>My Position</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>My Station Position</string> <string>My Station Position</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0"> <item row="1" column="1">
<widget class="QLabel" name="latitudeLabel">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="toolTip">
<string>Longitude in decimal degrees</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="latitudeSpinBox"> <widget class="QDoubleSpinBox" name="latitudeSpinBox">
<property name="toolTip"> <property name="toolTip">
<string>Latitude in decimal degrees</string> <string>Latitude in decimal degrees</string>
@ -72,24 +52,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="1" column="0">
<widget class="QSpinBox" name="altitudeSpinBox"> <widget class="QLabel" name="latitudeLabel">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text"> <property name="text">
<string>Altitude (m)</string> <string>Latitude</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="name"> <widget class="QLineEdit" name="name">
<property name="toolTip"> <property name="toolTip">
<string>Enter the name of your station</string> <string>Enter the name of your station</string>
@ -99,6 +69,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text">
<string>Altitude (m)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="nameLabel"> <widget class="QLabel" name="nameLabel">
<property name="text"> <property name="text">
@ -106,19 +90,66 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QCheckBox" name="autoUpdatePosition">
<property name="toolTip">
<string>Automatically update position using GPS (when available)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="toolTip">
<string>Longitude in decimal degrees</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="autoUpdatePositionLabel">
<property name="text">
<string>Auto-update from GPS</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="gps">
<property name="toolTip">
<string>Set position using GPS (if available)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/gps.png</normaloff>:/gps.png</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="altitudeSpinBox">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<tabstops> <tabstops>
@ -127,7 +158,9 @@
<tabstop>longitudeSpinBox</tabstop> <tabstop>longitudeSpinBox</tabstop>
<tabstop>altitudeSpinBox</tabstop> <tabstop>altitudeSpinBox</tabstop>
</tabstops> </tabstops>
<resources/> <resources>
<include location="../resources/res.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB // // Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths // // written by Edouard Griffiths //
// // // //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify // // 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 // // it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or // // the Free Software Foundation as version 3 of the License, or //
@ -21,7 +18,9 @@
#include "gui/mypositiondialog.h" #include "gui/mypositiondialog.h"
#include "ui_myposdialog.h" #include "ui_myposdialog.h"
#include "maincore.h"
#include <QGeoCoordinate>
MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent) : MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent) :
QDialog(parent), QDialog(parent),
@ -33,6 +32,7 @@ MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent)
ui->latitudeSpinBox->setValue(m_mainSettings.getLatitude()); ui->latitudeSpinBox->setValue(m_mainSettings.getLatitude());
ui->longitudeSpinBox->setValue(m_mainSettings.getLongitude()); ui->longitudeSpinBox->setValue(m_mainSettings.getLongitude());
ui->altitudeSpinBox->setValue(m_mainSettings.getAltitude()); ui->altitudeSpinBox->setValue(m_mainSettings.getAltitude());
ui->autoUpdatePosition->setChecked(m_mainSettings.getAutoUpdatePosition());
} }
MyPositionDialog::~MyPositionDialog() MyPositionDialog::~MyPositionDialog()
@ -46,5 +46,22 @@ void MyPositionDialog::accept()
m_mainSettings.setLatitude(ui->latitudeSpinBox->value()); m_mainSettings.setLatitude(ui->latitudeSpinBox->value());
m_mainSettings.setLongitude(ui->longitudeSpinBox->value()); m_mainSettings.setLongitude(ui->longitudeSpinBox->value());
m_mainSettings.setAltitude(ui->altitudeSpinBox->value()); m_mainSettings.setAltitude(ui->altitudeSpinBox->value());
m_mainSettings.setAutoUpdatePosition(ui->autoUpdatePosition->isChecked());
QDialog::accept(); QDialog::accept();
} }
void MyPositionDialog::on_gps_clicked()
{
const QGeoPositionInfo& position = MainCore::instance()->getPosition();
if (position.isValid())
{
QGeoCoordinate coord = position.coordinate();
ui->latitudeSpinBox->setValue(coord.latitude());
ui->longitudeSpinBox->setValue(coord.longitude());
ui->altitudeSpinBox->setValue(coord.altitude());
}
else
{
qDebug() << "MyPositionDialog::on_gps_clicked: Position is not valid.";
}
}

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB // // Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths // // written by Edouard Griffiths //
// // // //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify // // 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 // // it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or // // the Free Software Foundation as version 3 of the License, or //
@ -43,6 +40,7 @@ private:
private slots: private slots:
void accept(); void accept();
void on_gps_clicked();
}; };
#endif /* SDRBASE_GUI_MYPOSITIONDIALOG_H_ */ #endif /* SDRBASE_GUI_MYPOSITIONDIALOG_H_ */

View File

@ -22,7 +22,7 @@
#include "samplingdevicedialog.h" #include "samplingdevicedialog.h"
#include "ui_samplingdevicedialog.h" #include "ui_samplingdevicedialog.h"
#include "device/deviceenumerator.h" #include "device/deviceenumerator.h"
#include "maincore.h"
SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) : SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
QDialog(parent), QDialog(parent),
@ -32,19 +32,7 @@ SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
m_hasChanged(false) m_hasChanged(false)
{ {
ui->setupUi(this); ui->setupUi(this);
on_refreshDevices_clicked();
QList<QString> deviceDisplayNames;
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
QStringList devicesNamesList(deviceDisplayNames);
ui->deviceSelect->addItems(devicesNamesList);
} }
SamplingDeviceDialog::~SamplingDeviceDialog() SamplingDeviceDialog::~SamplingDeviceDialog()
@ -58,6 +46,23 @@ int SamplingDeviceDialog::exec()
return QDialog::exec(); return QDialog::exec();
} }
void SamplingDeviceDialog::displayDevices()
{
QList<QString> deviceDisplayNames;
m_deviceIndexes.clear();
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
ui->deviceSelect->clear();
ui->deviceSelect->addItems(deviceDisplayNames);
}
void SamplingDeviceDialog::setSelectedDeviceIndex(int deviceIndex) void SamplingDeviceDialog::setSelectedDeviceIndex(int deviceIndex)
{ {
ui->deviceSelect->blockSignals(true); ui->deviceSelect->blockSignals(true);
@ -77,6 +82,21 @@ void SamplingDeviceDialog::on_deviceSelect_currentIndexChanged(int index)
m_hasChanged = true; m_hasChanged = true;
} }
void SamplingDeviceDialog::on_refreshDevices_clicked()
{
PluginManager *pluginManager = MainCore::instance()->getPluginManager();
if (m_deviceType == 0) {
DeviceEnumerator::instance()->enumerateRxDevices(pluginManager);
} else if (m_deviceType == 1) {
DeviceEnumerator::instance()->enumerateTxDevices(pluginManager);
} else if (m_deviceType == 2) {
DeviceEnumerator::instance()->enumerateMIMODevices(pluginManager);
}
displayDevices();
}
void SamplingDeviceDialog::accept() void SamplingDeviceDialog::accept()
{ {
m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()]; m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()];

View File

@ -50,8 +50,11 @@ private:
std::vector<int> m_deviceIndexes; std::vector<int> m_deviceIndexes;
bool m_hasChanged; bool m_hasChanged;
void displayDevices();
private slots: private slots:
void on_deviceSelect_currentIndexChanged(int index); void on_deviceSelect_currentIndexChanged(int index);
void on_refreshDevices_clicked();
void accept(); void accept();
void reject(); void reject();
}; };

View File

@ -31,8 +31,8 @@
<property name="title"> <property name="title">
<string>Select from list</string> <string>Select from list</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item row="0" column="1"> <item>
<widget class="QComboBox" name="deviceSelect"> <widget class="QComboBox" name="deviceSelect">
<property name="font"> <property name="font">
<font> <font>
@ -42,6 +42,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="refreshDevices">
<property name="toolTip">
<string>Refresh list of devices</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -66,7 +80,9 @@
<tabstops> <tabstops>
<tabstop>buttonBox</tabstop> <tabstop>buttonBox</tabstop>
</tabstops> </tabstops>
<resources/> <resources>
<include location="../resources/res.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -501,7 +501,7 @@ void Workspace::stackVerticalSubWindows()
// Calculate spare vertical space, to be shared between non-fixed windows // Calculate spare vertical space, to be shared between non-fixed windows
int spareSpacePerWindow; int spareSpacePerWindow;
if (requiresVScrollBar) { if (requiresVScrollBar || (nonFixedWindows == 0)) {
spareSpacePerWindow = 0; spareSpacePerWindow = 0;
} else { } else {
spareSpacePerWindow = (mdiSize.height() - minHeight) / nonFixedWindows; spareSpacePerWindow = (mdiSize.height() - minHeight) / nonFixedWindows;

View File

@ -18,7 +18,9 @@
#include <QInputDialog> #include <QInputDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QProgressDialog>
#include <QLabel> #include <QLabel>
#include <QToolButton>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QFileDialog> #include <QFileDialog>
@ -31,10 +33,11 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QDesktopServices> #include <QDesktopServices>
#include <QProcess> #include <QProcess>
#include <QDirIterator>
#include <QAction> #include <QAction>
#include <QMenuBar> #include <QMenuBar>
#include <QStatusBar> #include <QStatusBar>
#include <QScreen>
#include "device/devicegui.h" #include "device/devicegui.h"
#include "device/deviceapi.h" #include "device/deviceapi.h"
@ -68,6 +71,8 @@
#include "gui/devicesetpresetsdialog.h" #include "gui/devicesetpresetsdialog.h"
#include "gui/commandsdialog.h" #include "gui/commandsdialog.h"
#include "gui/configurationsdialog.h" #include "gui/configurationsdialog.h"
#include "gui/dialogpositioner.h"
#include "gui/welcomedialog.h"
#include "dsp/dspengine.h" #include "dsp/dspengine.h"
#include "dsp/spectrumvis.h" #include "dsp/spectrumvis.h"
#include "dsp/dspcommands.h" #include "dsp/dspcommands.h"
@ -85,6 +90,9 @@
#include "webapi/webapiserver.h" #include "webapi/webapiserver.h"
#include "webapi/webapiadapter.h" #include "webapi/webapiadapter.h"
#include "commands/command.h" #include "commands/command.h"
#ifdef ANDROID
#include "util/android.h"
#endif
#include "mainwindow.h" #include "mainwindow.h"
@ -107,9 +115,17 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
m_mainCore(MainCore::instance()), m_mainCore(MainCore::instance()),
m_dspEngine(DSPEngine::instance()), m_dspEngine(DSPEngine::instance()),
m_lastEngineState(DeviceAPI::StNotStarted), m_lastEngineState(DeviceAPI::StNotStarted),
m_dateTimeWidget(nullptr),
m_showSystemWidget(nullptr),
m_commandKeyReceiver(nullptr), m_commandKeyReceiver(nullptr),
m_fftWisdomProcess(nullptr) m_fftWisdomProcess(nullptr)
{ {
#ifdef ANDROID
bool showWelcome = true;
#else
bool showWelcome = false;
#endif
qDebug() << "MainWindow::MainWindow: start"; qDebug() << "MainWindow::MainWindow: start";
setWindowTitle("SDRangel"); setWindowTitle("SDRangel");
@ -134,10 +150,21 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
splash->showStatusMessage("starting...", Qt::white); splash->showStatusMessage("starting...", Qt::white);
setWindowIcon(QIcon(":/sdrangel_icon.png")); setWindowIcon(QIcon(":/sdrangel_icon.png"));
createMenuBar(); #ifndef ANDROID
createStatusBar(); // To save screen space on Android, don't have menu bar. Instead menus are accessed via toolbar button
createMenuBar(nullptr);
createStatusBar();
#endif
#ifdef ANDROID
if (screen()->isLandscape(screen()->primaryOrientation())) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#else
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West); setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
#endif
setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
@ -146,6 +173,12 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection);
connect(screen(), &QScreen::orientationChanged, this, &MainWindow::orientationChanged);
screen()->setOrientationUpdateMask(Qt::PortraitOrientation
| Qt::LandscapeOrientation
| Qt::InvertedPortraitOrientation
| Qt::InvertedLandscapeOrientation);
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000); m_statusTimer.start(1000);
@ -209,6 +242,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
{ {
qDebug() << "MainWindow::MainWindow: no or empty current configuration, creating empty workspace..."; qDebug() << "MainWindow::MainWindow: no or empty current configuration, creating empty workspace...";
addWorkspace(); addWorkspace();
// If no configurations, load some basic examples
if (m_mainCore->m_settings.getConfigurations()->size() == 0) {
loadDefaultConfigurations();
}
}
else
{
// Only show welcome dialog first time program is run
showWelcome = false;
} }
} }
@ -240,6 +283,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
restoreGeometry(qUncompress(QByteArray::fromBase64(s.value("mainWindowGeometry").toByteArray()))); restoreGeometry(qUncompress(QByteArray::fromBase64(s.value("mainWindowGeometry").toByteArray())));
restoreState(qUncompress(QByteArray::fromBase64(s.value("mainWindowState").toByteArray()))); restoreState(qUncompress(QByteArray::fromBase64(s.value("mainWindowState").toByteArray())));
if (showWelcome)
{
// Show welcome dialog
WelcomeDialog welcomeDialog(this);
new DialogPositioner(&welcomeDialog, true);
welcomeDialog.exec();
// Show configurations
openConfigurationDialog(true);
}
qDebug() << "MainWindow::MainWindow: end"; qDebug() << "MainWindow::MainWindow: end";
} }
@ -340,6 +393,11 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
#ifdef ANDROID
// Seemingly needed on some versions of Android, otherwise the new windows aren't always displayed??
deviceWorkspace->repaint();
#endif
} }
void MainWindow::sampleSourceCreate( void MainWindow::sampleSourceCreate(
@ -1165,7 +1223,9 @@ void MainWindow::loadSettings()
m_mainCore->m_settings.load(); m_mainCore->m_settings.load();
m_mainCore->m_settings.sortPresets(); m_mainCore->m_settings.sortPresets();
m_mainCore->m_settings.sortCommands(); m_mainCore->m_settings.sortCommands();
m_mainCore->setLoggingOptions(); if (m_mainCore->m_logger) {
m_mainCore->setLoggingOptions();
}
} }
void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex) void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex)
@ -1230,6 +1290,59 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat
featureUI->saveFeatureSetSettings(preset); featureUI->saveFeatureSetSettings(preset);
} }
void MainWindow::loadDefaultConfigurations()
{
QDirIterator configurationsIt(":configurations", QDirIterator::Subdirectories);
while (configurationsIt.hasNext())
{
QString group = configurationsIt.next();
QDirIterator groupIt(group, {"*.cfgx"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Configuration* configuration = MainCore::instance()->m_settings.newConfiguration("", "");
configuration->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open configuration " << file.fileName();
}
}
}
QDirIterator presetIt(":presets", QDirIterator::Subdirectories);
while (presetIt.hasNext())
{
QString group = presetIt.next();
QDirIterator groupIt(group, {"*.prex"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Preset* preset = MainCore::instance()->m_settings.newPreset("", "");
preset->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open preset " << file.fileName();
}
}
}
}
void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog) void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog)
{ {
qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)", qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)",
@ -1240,23 +1353,26 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
configuration->getFeatureSetPreset().getFeatureCount() configuration->getFeatureSetPreset().getFeatureCount()
); );
QMessageBox *waitBox = nullptr; QProgressDialog *waitBox = nullptr;
if (fromDialog) if (fromDialog)
{ {
waitBox = new QMessageBox(this); waitBox = new QProgressDialog("Loading configuration...", "", 0, 100, this);
waitBox->setStandardButtons(QMessageBox::NoButton); waitBox->setWindowModality(Qt::WindowModal);
waitBox->setWindowModality(Qt::NonModal); waitBox->setAttribute(Qt::WA_DeleteOnClose, true);
waitBox->setIcon(QMessageBox::Information); waitBox->setMinimumDuration(0);
waitBox->setText("Loading configuration "); waitBox->setCancelButton(nullptr);
waitBox->setInformativeText("Deleting existing..."); waitBox->setValue(1);
waitBox->setWindowTitle("Please wait"); QApplication::processEvents();
waitBox->show();
waitBox->raise();
} }
// Wipe out everything first // Wipe out everything first
if (waitBox)
{
waitBox->setLabelText("Deleting existing...");
waitBox->setValue(5);
QApplication::processEvents();
}
// Device sets // Device sets
while (m_deviceUIs.size() > 0) { while (m_deviceUIs.size() > 0) {
removeLastDeviceSet(); removeLastDeviceSet();
@ -1276,6 +1392,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
{ {
addWorkspace(); addWorkspace();
m_workspaces[i]->setAutoStackOption(configuration->getWorkspaceAutoStackOptions()[i]); m_workspaces[i]->setAutoStackOption(configuration->getWorkspaceAutoStackOptions()[i]);
m_workspaces[i]->setTabSubWindowsOption(configuration->getWorkspaceTabSubWindowsOptions()[i]);
} }
if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces
@ -1283,8 +1400,11 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
} }
// Device sets // Device sets
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Loading device sets..."); {
waitBox->setLabelText("Loading device sets...");
waitBox->setValue(25);
QApplication::processEvents();
} }
const QList<Preset>& deviceSetPresets = configuration->getDeviceSetPresets(); const QList<Preset>& deviceSetPresets = configuration->getDeviceSetPresets();
@ -1354,11 +1474,20 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry()); m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry());
m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry()); m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry());
m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr);
if (waitBox)
{
waitBox->setValue(waitBox->value() + 50/deviceSetPresets.size());
QApplication::processEvents();
}
} }
// Features // Features
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Loading device sets..."); {
waitBox->setLabelText("Loading feature sets...");
waitBox->setValue(75);
QApplication::processEvents();
} }
m_featureUIs[0]->loadFeatureSetSettings( m_featureUIs[0]->loadFeatureSetSettings(
@ -1381,8 +1510,11 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
} }
// Lastly restore workspaces geometry // Lastly restore workspaces geometry
if (waitBox) { if (waitBox)
waitBox->setInformativeText("Finalizing..."); {
waitBox->setValue(90);
waitBox->setLabelText("Finalizing...");
QApplication::processEvents();
} }
for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++) for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++)
@ -1390,12 +1522,16 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->adjustSubWindowsAfterRestore(); m_workspaces[i]->adjustSubWindowsAfterRestore();
#ifdef ANDROID
// On Android, workspaces seem to be restored to 0,20, rather than 0,0
m_workspaces[i]->move(m_workspaces[i]->pos().x(), 0);
// Need to call updateGeometry, otherwise sometimes the layout is corrupted
m_workspaces[i]->updateGeometry();
#endif
} }
if (waitBox) if (waitBox) {
{ waitBox->setValue(100);
waitBox->close();
delete waitBox;
} }
} }
@ -1430,6 +1566,7 @@ void MainWindow::saveConfiguration(Configuration *configuration)
{ {
configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry()); configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry());
configuration->getWorkspaceAutoStackOptions().push_back(workspace->getAutoStackOption()); configuration->getWorkspaceAutoStackOptions().push_back(workspace->getAutoStackOption());
configuration->getWorkspaceTabSubWindowsOptions().push_back(workspace->getTabSubWindowsOption());
} }
} }
@ -1461,24 +1598,46 @@ QString MainWindow::openGLVersion()
} }
} }
void MainWindow::createMenuBar() void MainWindow::createMenuBar(QToolButton *button)
{ {
QMenuBar *menuBar = this->menuBar(); QMenu *fileMenu, *viewMenu, *workspacesMenu, *preferencesMenu, *helpMenu;
if (button == nullptr)
{
QMenuBar *menuBar = this->menuBar();
fileMenu = menuBar->addMenu("&File");
viewMenu = menuBar->addMenu("&View");
workspacesMenu = menuBar->addMenu("&Workspaces");
preferencesMenu = menuBar->addMenu("&Preferences");
helpMenu = menuBar->addMenu("&Help");
}
else
{
QMenu *menu = new QMenu();
fileMenu = new QMenu("&File");
menu->addMenu(fileMenu);
viewMenu = new QMenu("&View");
menu->addMenu(viewMenu);
workspacesMenu = new QMenu("&Workspaces");
menu->addMenu(workspacesMenu);
preferencesMenu = new QMenu("&Preferences");
menu->addMenu(preferencesMenu);
helpMenu = new QMenu("&Help");
menu->addMenu(helpMenu);
button->setMenu(menu);
}
QMenu *fileMenu = menuBar->addMenu("&File");
QAction *exitAction = fileMenu->addAction("E&xit"); QAction *exitAction = fileMenu->addAction("E&xit");
exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
exitAction->setToolTip("Exit"); exitAction->setToolTip("Exit");
QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close); QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close);
QMenu *viewMenu = menuBar->addMenu("&View");
QAction *fullscreenAction = viewMenu->addAction("&Fullscreen"); QAction *fullscreenAction = viewMenu->addAction("&Fullscreen");
fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11)); fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11));
fullscreenAction->setToolTip("Toggle fullscreen view"); fullscreenAction->setToolTip("Toggle fullscreen view");
fullscreenAction->setCheckable(true); fullscreenAction->setCheckable(true);
QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled); QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled);
QMenu *workspacesMenu = menuBar->addMenu("&Workspaces");
QAction *newWorkspaceAction = workspacesMenu->addAction("&New"); QAction *newWorkspaceAction = workspacesMenu->addAction("&New");
newWorkspaceAction->setToolTip("Add a new workspace"); newWorkspaceAction->setToolTip("Add a new workspace");
QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace); QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace);
@ -1489,7 +1648,6 @@ void MainWindow::createMenuBar()
removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces"); removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces");
QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces); QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces);
QMenu *preferencesMenu = menuBar->addMenu("&Preferences");
QAction *configurationsAction = preferencesMenu->addAction("&Configurations..."); QAction *configurationsAction = preferencesMenu->addAction("&Configurations...");
configurationsAction->setToolTip("Manage configurations"); configurationsAction->setToolTip("Manage configurations");
QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered); QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered);
@ -1519,7 +1677,6 @@ void MainWindow::createMenuBar()
saveAllAction->setToolTip("Save all current settings"); saveAllAction->setToolTip("Save all current settings");
QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered); QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered);
QMenu *helpMenu = menuBar->addMenu("&Help");
QAction *quickStartAction = helpMenu->addAction("&Quick start..."); QAction *quickStartAction = helpMenu->addAction("&Quick start...");
quickStartAction->setToolTip("Instructions for quick start"); quickStartAction->setToolTip("Instructions for quick start");
QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered); QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered);
@ -1882,7 +2039,11 @@ void MainWindow::handleWorkspaceVisibility(Workspace *workspace, bool visibility
void MainWindow::addWorkspace() void MainWindow::addWorkspace()
{ {
int workspaceIndex = m_workspaces.size(); int workspaceIndex = m_workspaces.size();
m_workspaces.push_back(new Workspace(workspaceIndex)); Workspace *workspace = new Workspace(workspaceIndex);
m_workspaces.push_back(workspace);
if (workspace->getMenuButton()) {
createMenuBar(workspace->getMenuButton());
}
QStringList featureNames; QStringList featureNames;
m_pluginManager->listFeatures(featureNames); m_pluginManager->listFeatures(featureNames);
m_workspaces.back()->addAvailableFeatures(featureNames); m_workspaces.back()->addAvailableFeatures(featureNames);
@ -1923,6 +2084,13 @@ void MainWindow::addWorkspace()
&MainWindow::openFeaturePresetsDialog &MainWindow::openFeaturePresetsDialog
); );
QObject::connect(
m_workspaces.back(),
&Workspace::configurationPresetsDialogRequested,
this,
&MainWindow::on_action_Configurations_triggered
);
QObject::connect( QObject::connect(
m_workspaces.back(), m_workspaces.back(),
&Workspace::startAllDevices, &Workspace::startAllDevices,
@ -1951,7 +2119,7 @@ void MainWindow::addWorkspace()
// tabBars.back()->setStyleSheet("QTabBar::tab:selected { background: rgb(128,70,0); }"); // change text color so it is visible // tabBars.back()->setStyleSheet("QTabBar::tab:selected { background: rgb(128,70,0); }"); // change text color so it is visible
// } // }
} }
} }
void MainWindow::viewAllWorkspaces() void MainWindow::viewAllWorkspaces()
{ {
@ -2006,6 +2174,13 @@ void MainWindow::removeEmptyWorkspaces()
} }
} }
} }
#ifdef ANDROID
// Need at least one workspace on Android, as no menus without
if (m_workspaces.size() == 0) {
addWorkspace();
}
#endif
} }
void MainWindow::on_action_View_Fullscreen_toggled(bool checked) void MainWindow::on_action_View_Fullscreen_toggled(bool checked)
@ -2063,7 +2238,12 @@ void MainWindow::on_action_Loaded_Plugins_triggered()
void MainWindow::on_action_Configurations_triggered() void MainWindow::on_action_Configurations_triggered()
{ {
ConfigurationsDialog dialog(this); openConfigurationDialog(false);
}
void MainWindow::openConfigurationDialog(bool openOnly)
{
ConfigurationsDialog dialog(openOnly, this);
dialog.setConfigurations(m_mainCore->m_settings.getConfigurations()); dialog.setConfigurations(m_mainCore->m_settings.getConfigurations());
dialog.populateTree(); dialog.populateTree();
QObject::connect( QObject::connect(
@ -2078,24 +2258,28 @@ void MainWindow::on_action_Configurations_triggered()
this, this,
[=](const Configuration* configuration) { this->loadConfiguration(configuration, true); } [=](const Configuration* configuration) { this->loadConfiguration(configuration, true); }
); );
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
} }
void MainWindow::on_action_Audio_triggered() void MainWindow::on_action_Audio_triggered()
{ {
AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this); AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this);
new DialogPositioner(&audioDialog, true);
audioDialog.exec(); audioDialog.exec();
} }
void MainWindow::on_action_Graphics_triggered() void MainWindow::on_action_Graphics_triggered()
{ {
GraphicsDialog graphicsDialog(m_mainCore->m_settings, this); GraphicsDialog graphicsDialog(m_mainCore->m_settings, this);
new DialogPositioner(&graphicsDialog, true);
graphicsDialog.exec(); graphicsDialog.exec();
} }
void MainWindow::on_action_Logging_triggered() void MainWindow::on_action_Logging_triggered()
{ {
LoggingDialog loggingDialog(m_mainCore->m_settings, this); LoggingDialog loggingDialog(m_mainCore->m_settings, this);
new DialogPositioner(&loggingDialog, true);
loggingDialog.exec(); loggingDialog.exec();
m_mainCore->setLoggingOptions(); m_mainCore->setLoggingOptions();
} }
@ -2103,6 +2287,7 @@ void MainWindow::on_action_Logging_triggered()
void MainWindow::on_action_My_Position_triggered() void MainWindow::on_action_My_Position_triggered()
{ {
MyPositionDialog myPositionDialog(m_mainCore->m_settings, this); MyPositionDialog myPositionDialog(m_mainCore->m_settings, this);
new DialogPositioner(&myPositionDialog, true);
myPositionDialog.exec(); myPositionDialog.exec();
} }
@ -2110,6 +2295,7 @@ void MainWindow::on_action_DeviceUserArguments_triggered()
{ {
qDebug("MainWindow::on_action_DeviceUserArguments_triggered"); qDebug("MainWindow::on_action_DeviceUserArguments_triggered");
DeviceUserArgsDialog deviceUserArgsDialog(DeviceEnumerator::instance(), m_mainCore->m_settings.getDeviceUserArgs(), this); DeviceUserArgsDialog deviceUserArgsDialog(DeviceEnumerator::instance(), m_mainCore->m_settings.getDeviceUserArgs(), this);
new DialogPositioner(&deviceUserArgsDialog, true);
deviceUserArgsDialog.exec(); deviceUserArgsDialog.exec();
} }
@ -2121,6 +2307,7 @@ void MainWindow::on_action_commands_triggered()
commandsDialog.setApiPort(m_apiServer->getPort()); commandsDialog.setApiPort(m_apiServer->getPort());
commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver); commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver);
commandsDialog.populateTree(); commandsDialog.populateTree();
new DialogPositioner(&commandsDialog, true);
commandsDialog.exec(); commandsDialog.exec();
} }
@ -2140,6 +2327,7 @@ void MainWindow::on_action_FFT_triggered()
this, this,
SLOT(fftWisdomProcessFinished(int, QProcess::ExitStatus))); SLOT(fftWisdomProcessFinished(int, QProcess::ExitStatus)));
FFTWisdomDialog fftWisdomDialog(m_fftWisdomProcess, this); FFTWisdomDialog fftWisdomDialog(m_fftWisdomProcess, this);
new DialogPositioner(&fftWisdomDialog, true);
if (fftWisdomDialog.exec() == QDialog::Rejected) if (fftWisdomDialog.exec() == QDialog::Rejected)
{ {
@ -2790,6 +2978,7 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace)
dialog.setWorkspaces(&m_workspaces); dialog.setWorkspaces(&m_workspaces);
dialog.populateTree(); dialog.populateTree();
dialog.move(p); dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
if (dialog.wasPresetLoaded()) if (dialog.wasPresetLoaded())
@ -2820,6 +3009,7 @@ void MainWindow::openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI)
dialog.setWorkspaces(&m_workspaces); dialog.setWorkspaces(&m_workspaces);
dialog.populateTree((int) deviceGUI->getDeviceType()); dialog.populateTree((int) deviceGUI->getDeviceType());
dialog.move(p); dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec(); dialog.exec();
} }
@ -2840,7 +3030,9 @@ void MainWindow::on_action_About_triggered()
void MainWindow::updateStatus() void MainWindow::updateStatus()
{ {
m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t")); if (m_dateTimeWidget) {
m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t"));
}
} }
void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release) void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release)
@ -2862,3 +3054,30 @@ void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifie
} }
} }
} }
void MainWindow::keyPressEvent(QKeyEvent* event)
{
#ifdef ANDROID
if (event->key() == Qt::Key_Back)
{
// On Android, we don't want to exit when back key is pressed, just run in the background
Android::moveTaskToBack();
}
else
#endif
{
QMainWindow::keyPressEvent(event);
}
}
void MainWindow::orientationChanged(Qt::ScreenOrientation orientation)
{
#ifdef ANDROID
// Adjust workspace tab position, to leave max space for MDI windows
if ((orientation == Qt::LandscapeOrientation) || (orientation == Qt::InvertedLandscapeOrientation)) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#endif
}

View File

@ -34,6 +34,7 @@
class QLabel; class QLabel;
class QTreeWidgetItem; class QTreeWidgetItem;
class QDir; class QDir;
class QToolButton;
class DSPEngine; class DSPEngine;
class DSPDeviceSourceEngine; class DSPDeviceSourceEngine;
@ -136,7 +137,7 @@ private:
void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex);
QString openGLVersion(); QString openGLVersion();
void createMenuBar(); void createMenuBar(QToolButton *button);
void createStatusBar(); void createStatusBar();
void closeEvent(QCloseEvent*); void closeEvent(QCloseEvent*);
void applySettings(); void applySettings();
@ -171,6 +172,9 @@ private:
bool handleMessage(const Message& cmd); bool handleMessage(const Message& cmd);
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
private slots: private slots:
void handleMessages(); void handleMessages();
void handleWorkspaceVisibility(Workspace *workspace, bool visibility); void handleWorkspaceVisibility(Workspace *workspace, bool visibility);
@ -194,6 +198,8 @@ private slots:
void addWorkspace(); void addWorkspace();
void viewAllWorkspaces(); void viewAllWorkspaces();
void removeEmptyWorkspaces(); void removeEmptyWorkspaces();
void openConfigurationDialog(bool openOnly);
void loadDefaultConfigurations();
void loadConfiguration(const Configuration *configuration, bool fromDialog = false); void loadConfiguration(const Configuration *configuration, bool fromDialog = false);
void saveConfiguration(Configuration *configuration); void saveConfiguration(Configuration *configuration);
void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex); void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex);
@ -215,6 +221,7 @@ private slots:
void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI); void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI);
void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release); void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release);
void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void orientationChanged(Qt::ScreenOrientation orientation);
}; };
#endif // INCLUDE_MAINWINDOW_H #endif // INCLUDE_MAINWINDOW_H

View File

@ -0,0 +1 @@
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

View File

@ -0,0 +1 @@
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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgNEQUKAAxuQAAEBcAEHZGVmYXVsdHACB25vIG5hbWUAZAAAZAEBgGVCAdnQywADAAAAAAAAAAAAAAAAAZoAAAMlAAAAAAAAAAAAAAGaAAADJQAAAAAAAAAAAZsAAAAAAAAAAAAAAZoAAAMlAMgBAYHJBB2QAAEBcAEHZGVmYXVsdHACB25vIG5hbWUwAwQNcx2AgAQAgAXikAABAQABAgQAAAIAAAMBBEAEBAAAAABABQRCjAAAYAYBAWAHAQFgCAEAYAkBAAAKAQFgCwEBAA0BEgAOAQEADwEeYBABAQARATJAEgQ/KPXDABMBAQAUAQpgFQEAcBYJMTI3LjAuMC4xEBcCIrdgGAEAYBkBAQAaATJgGwEAABwBAmAdAQAAHgBgHwEAACABA3AhBUFuZ2VsACIBAgAjAAAkAicQACUCJxAAJgInEAAnAQVgKgEBACsBBQAsAQEALQEBAC4AYC8BAGAwAQAAZAAAbgCAKAQAAAAAgCkEAAAAAGAGAQEABwBgCAEBgAlCAdnQywADAAAAABOIAAAAAAAAFQMAAAFuAAATiAAAAAAAABUDAAABbv////8AAAAAAZsAABOIAAAAAAAAFQMAAAFuAAoAgAtCAdnQywADAAAAABOIAAAAAAAAFPMAAAEEAAATiAAAAAAAABTzAAABBP////8AAAAAAZsAABOIAAAAAAAAFPMAAAEEAAwAcA0cc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAOCDAwMDAwMDAxAA8AABAAABQBAXAYHHNkcmFuZ2VsLnNhbXBsZXNvdXJjZS5ydGxzZHJwGQgwMDAwMDAwMQAaAIAbXpAAAQEAAgIBpAADABAEAGAFAQBgBgEAAAcBAgAIAx9AAGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5kYWJkZW1vZIHKAZWQAAEBAAEAAAIAcAMAQAQESbufQEAFBECgAABgBgEAcAcVU3lzdGVtIGRlZmF1bHQgZGV2aWNlgAgjkAABAQABABACBP9NaRlwAw9EQUIgRGVtb2R1bGF0b3IABwAQCQT/TWkZcAoPREFCIERlbW9kdWxhdG9yYAsBAHAMCTEyNy4wLjAuMRANAiK4EA4AEA8AgBCeAAAA/wAAAAAAAAAEAAAAIgBzAGUAdAB0AGkAbgBnAHMAQwBvAG4AdABhAGkAbgBlAHIAAAABAAAAIABwAHIAbwBnAHIAYQBtAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAYwB1AHIAcgBlAG4AdABQAHIAbwBnAHIAYQBtAAAAAQAAABQAcwB0AGEAdABpAHMAdABpAGMAcwAAAAEAEQCAEkIB2dDLAAMAAAAAAAAAAAAAAAABmgAAAvwAAAAAAAAAAP////7////+AAAAAAIAAAABmwAAAAAAAAAAAAABmgAAAvxgEwEAAGQAAGUBAQBmAQIAyAH/AMkB/wDKAf8EASwBAWQBLQEABAGQAQFkAZEBAQ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE4MAMECDgI0IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE5MAMECCuk8IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

13
settings/settings.qrc Normal file
View File

@ -0,0 +1,13 @@
<RCC>
<qresource prefix="/">
<file>configurations/rtlsdr/adsb.cfgx</file>
<file>configurations/rtlsdr/ais.cfgx</file>
<file>configurations/rtlsdr/apt.cfgx</file>
<file>configurations/rtlsdr/dab.cfgx</file>
<file>configurations/rtlsdr/isspacket.cfgx</file>
<file>configurations/rtlsdr/radiosonde.cfgx</file>
<file>configurations/airspyhf/radioclockeu.cfgx</file>
<file>presets/rtlsdr/noaa18.prex</file>
<file>presets/rtlsdr/noaa19.prex</file>
</qresource>
</RCC>