diff --git a/LenaPi/.qmake.stash b/LenaPi/.qmake.stash new file mode 100644 index 0000000..8046afc --- /dev/null +++ b/LenaPi/.qmake.stash @@ -0,0 +1,24 @@ +QMAKE_CXX.QT_COMPILER_STDCXX = 201402L +QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 10 +QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 2 +QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 1 +QMAKE_CXX.COMPILER_MACROS = \ + QT_COMPILER_STDCXX \ + QMAKE_GCC_MAJOR_VERSION \ + QMAKE_GCC_MINOR_VERSION \ + QMAKE_GCC_PATCH_VERSION +QMAKE_CXX.INCDIRS = \ + /usr/arm-linux-gnueabihf/include/c++/10 \ + /usr/arm-linux-gnueabihf/include/c++/10/arm-linux-gnueabihf \ + /usr/arm-linux-gnueabihf/include/c++/10/backward \ + /usr/lib/gcc-cross/arm-linux-gnueabihf/10/include \ + /usr/arm-linux-gnueabihf/include \ + /usr/include/arm-linux-gnueabihf \ + /usr/include +QMAKE_CXX.LIBDIRS = \ + /usr/lib/gcc-cross/arm-linux-gnueabihf/10 \ + /usr/arm-linux-gnueabihf/lib \ + /lib/arm-linux-gnueabihf \ + /lib \ + /usr/lib/arm-linux-gnueabihf \ + /usr/lib diff --git a/LenaPi/EnergySaver.cpp b/LenaPi/EnergySaver.cpp index 556dcbc..da9db13 100644 --- a/LenaPi/EnergySaver.cpp +++ b/LenaPi/EnergySaver.cpp @@ -4,16 +4,35 @@ #include #include #include +EnergySaver::~EnergySaver() +{ +#ifdef ANDROID + releaseAndroidLock(); +#endif +} + +void EnergySaver::init() +{ + auto saver = instance(); + saver->mIsAutoShutDownEnabled = false; +#ifdef ANDROID + saver->initAdroidLocks(); +#endif +} void EnergySaver::init(int interval, const QString &shutdownScript) { + auto saver = instance(); + saver->mIsAutoShutDownEnabled = true; QFileInfo script(shutdownScript); if(script.exists()){ - auto saver = instance(); saver->setShutdownScript(shutdownScript); saver->initTimer(interval*1000); saver->restartTimer(); } +#ifdef ANDROID + saver->initAdroidLocks(); +#endif } @@ -21,15 +40,32 @@ void EnergySaver::init(int interval, const QString &shutdownScript) EnergySaver *EnergySaver::instance() { static EnergySaver* inst; - if (inst == nullptr) + if (!inst) { inst = new EnergySaver(); } return inst; } +void EnergySaver::deactivate() +{ + mIsActive = false; + mTimer.stop(); + setAndroidLock(); +} + +void EnergySaver::activate() +{ + mIsActive = true; + restartTimer(); + releaseAndroidLock(); +} + void EnergySaver::restartTimer() { + // Energy saver is currently deactivated -> Do NOT start timer + if(!mIsActive) return; + if(mTimer.isActive()){ mTimer.stop(); } @@ -49,9 +85,59 @@ void EnergySaver::setShutdownScript(const QString &shutdownScript) mShutdownScript = shutdownScript; } +void EnergySaver::initAdroidLocks() +{ +#ifdef ANDROID + QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); + if ( activity.isValid() ) + { + QAndroidJniObject serviceName = QAndroidJniObject::getStaticObjectField("android/content/Context","POWER_SERVICE"); + if ( serviceName.isValid() ) + { + QAndroidJniObject powerMgr = activity.callObjectMethod("getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;",serviceName.object()); + if ( powerMgr.isValid() ) + { + jint levelAndFlags = QAndroidJniObject::getStaticField("android/os/PowerManager","SCREEN_DIM_WAKE_LOCK"); + + QAndroidJniObject tag = QAndroidJniObject::fromString( "My Tag" ); + + m_wakeLock = powerMgr.callObjectMethod("newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;", levelAndFlags,tag.object()); + } + } + } +#endif +} + +void EnergySaver::setAndroidLock() +{ +#ifdef ANDROID + if ( m_wakeLock.isValid() ) + { + m_wakeLock.callMethod("acquire", "()V"); + qDebug() << "Locked device, can't go to standby anymore"; + } + else + { + assert( false ); + } +#endif +} + +void EnergySaver::releaseAndroidLock() +{ +#ifdef ANDROID + if ( m_wakeLock.isValid() ) + { + m_wakeLock.callMethod("release", "()V"); + qDebug() << "Unlocked device, can now go to standby"; + } +#endif +} + void EnergySaver::onTimeout() { + if(!mIsAutoShutDownEnabled) return; std::cout << "Shutting down."; #ifndef _DEBUG QProcess p; diff --git a/LenaPi/EnergySaver.h b/LenaPi/EnergySaver.h index 3e14fe4..fbfd503 100644 --- a/LenaPi/EnergySaver.h +++ b/LenaPi/EnergySaver.h @@ -4,27 +4,39 @@ #include #include +#ifdef ANDROID +#include +#endif + /** * @brief Class handling energy saving options. * - * Shut down device if no mouse input is detected and music player - * has not been active or a certain time interval. + * On Android devices, it locks the cpu shutdown while active. On other devices, it + * will shut down device on timeout if the options are set accordingly and a shutdown script + * is provided. * - * @todo For now this does only work for Lena's RasPi, where the - * shutdown script is positioned in a ceratin hardcoded path. - * Enable/disable energy saving option, timeout and path of - * shutdown script via config + * In the context of the LenaPi application, it will prevent cpu shutdown on android devices while playing + * music. On other devices, it will shutdown the device if no music has been playing and no mouse input was + * detected for a certain time intervall. */ class EnergySaver : public QObject { Q_OBJECT protected: - explicit EnergySaver(QObject *parent = nullptr) : QObject(parent) {} + using QObject::QObject; public: + ~EnergySaver(); + /** + * @brief Create instance if necessary. The instance will have auto shutdown disabeld. + * + * Used to prevent sleep on android devices. + */ + static void init(); /** * @brief Create instance if necessary, configure it and start timer. + * @param enabledAutoShutdown Defines whether device will shutdown on inactivity using shutdownScript * @param interval Timer interval in seconds * @param shutdownScript Path to shutdown script file * @see EnergySaver::instance @@ -43,7 +55,19 @@ public: public slots: /** - * @brief Restart shutdown timer, e.g. because of music player activiti + * @brief Deactivate energy saver, e.g., as music is currently playing + * + * Sets locks on adroid devics and stops shutdown-timer. + */ + void deactivate(); + /** + * @brief Active energy saver + * + * Releases locks on android devices and restarts shutdown-timer if shutdown option is set. + */ + void activate(); + /** + * @brief Restart shutdown timer, e.g. because of music player activity */ void restartTimer(); @@ -54,8 +78,31 @@ private: */ void initTimer(int interval); void setShutdownScript(const QString& shutdownScript); + /** + * @brief Initializes locking or Android devices + */ + void initAdroidLocks(); + /** + * @brief Sets locks on Android devices to prevent sleep. + * + * Used to prevent cpu sleep as otherwise music will stop. + */ + void setAndroidLock(); + /** + * @brief Releases locks on Android devices to allow the device's cpu to go into sleep mode. + */ + void releaseAndroidLock(); QTimer mTimer; QString mShutdownScript; + bool mIsActive{false}; + bool mIsAutoShutDownEnabled{false}; + +#ifdef ANDROID + /** + * @brief Prevent energy saving for Android devices + */ + QAndroidJniObject m_wakeLock; +#endif private slots: /** @@ -63,6 +110,7 @@ private slots: */ void onTimeout(); + }; #endif // ENERGYSAVER_H diff --git a/LenaPi/LenaPi.pro b/LenaPi/LenaPi.pro index 1bc3c7b..e603e36 100644 --- a/LenaPi/LenaPi.pro +++ b/LenaPi/LenaPi.pro @@ -1,12 +1,16 @@ TEMPLATE = app - +linux:android { +QT += androidextras +ANDROID_PERMISSIONS += android.permission.WAKE_LOCK +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-files +QMAKE_CXXFLAGS += -DANDROID=1 +} QT += qml quick multimedia CONFIG += c++11 LIBS += -ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-files SOURCES += main.cpp \ controllers/MusicPlayer.cpp \ - controllers/StyleController.cpp \ + controllers/StyleHandling.cpp \ models/NavigationListModel.cpp \ models/NavigationItemModel.cpp \ controllers/NavigationController.cpp \ @@ -42,7 +46,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin HEADERS += \ controllers/MusicPlayer.h \ - controllers/StyleController.h \ + controllers/StyleHandling.h \ models/MusicPlayer.h \ models/NavigationListModel.h \ models/NavigationItemModel.h \ diff --git a/LenaPi/LenaPi.pro.user b/LenaPi/LenaPi.pro.user index a1418e5..73f8196 100644 --- a/LenaPi/LenaPi.pro.user +++ b/LenaPi/LenaPi.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -87,6 +87,250 @@ ProjectExplorer.Project.Target.0 + + GenericLinuxOsType + armhf/raspi 10.0.1.146 + armhf/raspi 10.0.1.146 + {9f02fab2-7f72-4b24-bbd8-dbe33e863260} + 1 + 0 + 0 + + 0 + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Debug + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Release + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + + + 0 + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Profile + /home/jmr/privat/src/LenaPi/build-LenaPi-Unnamed-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + 3 + + + + true + RemoteLinux.CheckForFreeDiskSpaceStep + + + + + / + 5242880 + + + + + true + RemoteLinux.KillAppStep + + + + + + + + + true + RemoteLinux.RsyncDeployStep + + + + + + + -av + + 3 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + DeployToGenericLinux + + 1 + + true + true + true + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + + true + true + true + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + + 1 + + RemoteLinux.CustomRunConfig + + 1 + false + true + false + true + :0.0 + + 2 + + + + ProjectExplorer.Project.Target.1 Android.Device.Type Android Qt 5.15.2 (android) Clang Multi-Abi @@ -309,7 +553,6 @@ 0 - LenaPi Qt4ProjectManager.AndroidRunConfiguration:/home/jmr/privat/src/LenaPi/LenaPi/LenaPi.pro /home/jmr/privat/src/LenaPi/LenaPi/LenaPi.pro false @@ -321,7 +564,7 @@ - ProjectExplorer.Project.Target.1 + ProjectExplorer.Project.Target.2 Desktop desktop @@ -492,7 +735,7 @@ ProjectExplorer.Project.TargetCount - 2 + 3 ProjectExplorer.Project.Updater.FileVersion diff --git a/LenaPi/MouseEventSpy.cpp b/LenaPi/MouseEventSpy.cpp index 26eb916..95ade23 100644 --- a/LenaPi/MouseEventSpy.cpp +++ b/LenaPi/MouseEventSpy.cpp @@ -12,7 +12,7 @@ void MouseEventSpy::init() MouseEventSpy* MouseEventSpy::instance() { static MouseEventSpy* inst; - if (inst == nullptr) + if (!inst) { inst = new MouseEventSpy(); QGuiApplication* app = qGuiApp; diff --git a/LenaPi/android-files/AndroidManifest.xml b/LenaPi/android-files/AndroidManifest.xml index 0236a61..e0c143a 100644 --- a/LenaPi/android-files/AndroidManifest.xml +++ b/LenaPi/android-files/AndroidManifest.xml @@ -15,7 +15,7 @@ - + @@ -81,4 +81,6 @@ + + diff --git a/LenaPi/android-files/res/drawable-hdpi/icon.png b/LenaPi/android-files/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..b22cf59 Binary files /dev/null and b/LenaPi/android-files/res/drawable-hdpi/icon.png differ diff --git a/LenaPi/android-files/res/drawable-ldpi/icon.png b/LenaPi/android-files/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..7c9ca0f Binary files /dev/null and b/LenaPi/android-files/res/drawable-ldpi/icon.png differ diff --git a/LenaPi/android-files/res/drawable-mdpi/icon.png b/LenaPi/android-files/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..57a20d1 Binary files /dev/null and b/LenaPi/android-files/res/drawable-mdpi/icon.png differ diff --git a/LenaPi/android-files/res/drawable-xhdpi/icon.png b/LenaPi/android-files/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000..3ebfa96 Binary files /dev/null and b/LenaPi/android-files/res/drawable-xhdpi/icon.png differ diff --git a/LenaPi/android-files/res/drawable-xxhdpi/icon.png b/LenaPi/android-files/res/drawable-xxhdpi/icon.png new file mode 100644 index 0000000..aaf15bf Binary files /dev/null and b/LenaPi/android-files/res/drawable-xxhdpi/icon.png differ diff --git a/LenaPi/android-files/res/drawable-xxxhdpi/icon.png b/LenaPi/android-files/res/drawable-xxxhdpi/icon.png new file mode 100644 index 0000000..c8cf6cb Binary files /dev/null and b/LenaPi/android-files/res/drawable-xxxhdpi/icon.png differ diff --git a/LenaPi/controllers/NavigationController.cpp b/LenaPi/controllers/NavigationController.cpp index 0b04961..f3bb4ab 100644 --- a/LenaPi/controllers/NavigationController.cpp +++ b/LenaPi/controllers/NavigationController.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -19,6 +20,9 @@ NavigationController::NavigationController(QObject *parent) : QObject(parent), mUiState->showNavigation(); mNavList->navigateTo(item); }); + /* Connect player state to energy saver to prevent device shutdown while playing music. + */ + connect(mMediaPlayer, &MusicPlayer::isPlayingChanged, this, &NavigationController::startOrStopEnergySaverDependingOnPlayerState); } void NavigationController::setDebugOutput(const QString& text) @@ -111,3 +115,16 @@ void NavigationController::onNavigationRequest() mUiState->showMusicPlayer(); } } + +void NavigationController::startOrStopEnergySaverDependingOnPlayerState() +{ + auto* energySaver = EnergySaver::instance(); + assert(energySaver); + if(energySaver){ + if(mMediaPlayer->isPlaying()){ + energySaver->deactivate(); + } else { + energySaver->activate(); + } + } +} diff --git a/LenaPi/controllers/NavigationController.h b/LenaPi/controllers/NavigationController.h index 46fbdc2..0459f3d 100644 --- a/LenaPi/controllers/NavigationController.h +++ b/LenaPi/controllers/NavigationController.h @@ -86,6 +86,7 @@ private slots: * @brief Either show subdirectories or music player depending on current directory type */ void onNavigationRequest(); + void startOrStopEnergySaverDependingOnPlayerState(); }; #endif // NAVIGATIONCONTROLLER_H diff --git a/LenaPi/controllers/StyleController.cpp b/LenaPi/controllers/StyleHandling.cpp similarity index 78% rename from LenaPi/controllers/StyleController.cpp rename to LenaPi/controllers/StyleHandling.cpp index 764818f..19d4419 100644 --- a/LenaPi/controllers/StyleController.cpp +++ b/LenaPi/controllers/StyleHandling.cpp @@ -1,27 +1,29 @@ -#include "StyleController.h" +#include "StyleHandling.h" #include #include +#include -StyleController::StyleController(QObject *parent) +StyleHandling::StyleHandling(QObject *parent) : QObject{parent}, mStyleSizes(new QQmlPropertyMap(this)), mMargins(new QQmlPropertyMap(this)), mSpacings(new QQmlPropertyMap(this)), mPaddings(new QQmlPropertyMap(this)) { // nothing } -void StyleController::calculateAndSetRatio() +void StyleHandling::calculateAndSetRatio() { qreal refHeight = 480; qreal refWidth = 800; // Scales to fullscreen. No rescaling when changing window size QRect rect = QGuiApplication::primaryScreen()->geometry(); - qreal height = qMax(rect.width(),rect.height()); - qreal width = qMin(rect.width(), rect.height()); + qreal height = qMin(rect.width(),rect.height()); + qreal width = qMax(rect.width(), rect.height()); mRatio = qMin(height/refHeight, width/refWidth); + qDebug() << "mRation=" << mRatio<< "sizes="<insert(key, applyRatio(value)); } -int StyleController::applyRatio(int size) const +int StyleHandling::applyRatio(int size) const { return size*mRatio; } -void StyleController::init(QQmlContext *context) +void StyleHandling::init(QQmlContext *context) { calculateAndSetRatio(); initStyleSizes(); diff --git a/LenaPi/controllers/StyleController.h b/LenaPi/controllers/StyleHandling.h similarity index 93% rename from LenaPi/controllers/StyleController.h rename to LenaPi/controllers/StyleHandling.h index 4cc5ffd..2128b05 100644 --- a/LenaPi/controllers/StyleController.h +++ b/LenaPi/controllers/StyleHandling.h @@ -1,5 +1,5 @@ -#ifndef STYLECONTROLLER_H -#define STYLECONTROLLER_H +#ifndef STYLEHANDLING_H +#define STYLEHANDLING_H #include #include @@ -20,11 +20,11 @@ * @todo scale fonts as well? * @todo use dpi for scaling as app is very small on Android? */ -class StyleController : public QObject +class StyleHandling : public QObject { Q_OBJECT public: - explicit StyleController(QObject *parent = nullptr); + explicit StyleHandling(QObject *parent = nullptr); /** * @brief Calculates ratio, initializes all maps and registers them in QML context @@ -78,4 +78,4 @@ private: QQmlPropertyMap* mPaddings = nullptr; }; -#endif // STYLECONTROLLER_H +#endif // STYLEHANDLING_H diff --git a/LenaPi/main.cpp b/LenaPi/main.cpp index c1aafb2..b125dc7 100644 --- a/LenaPi/main.cpp +++ b/LenaPi/main.cpp @@ -4,18 +4,20 @@ #include #include #include -#include -#include "controllers/NavigationController.h" +#include +#include +#include #include "MouseEventSpy.h" #include "EnergySaver.h" -#include "controllers/SettingsHandler.h" int main(int argc, char *argv[]) { - QGuiApplication app(argc, argv); QQmlApplicationEngine engine; + /**************************************************************************** + * Configure and parse commandline arguments + ****************************************************************************/ QCommandLineParser parser; parser.setApplicationDescription("Lena's music app"); // Define a custom config file using -c or --config @@ -25,6 +27,12 @@ int main(int argc, char *argv[]) // process commandline arguments parser.process(app); + /**************************************************************************** + * Find and read settings + * If a config file is handed over via commandline arguments, it is preferred. + * Otherwise, the config in the standard location is used. If none exists yet, + * a default config is created. + ****************************************************************************/ QSettings* settings = nullptr; if(!parser.value(configOption).isEmpty()){ // config was handed over via commandline argument. Use this config if file exists. @@ -35,23 +43,35 @@ int main(int argc, char *argv[]) } } if(!settings){ - // default config + // create config from default location settings = new QSettings(QSettings::Scope::UserScope, "MaleyanaSoft", "LenaPi"); } - /* Read Settings */ + // Read Settings const auto settingsHandler = SettingsHandler::createSettingsHandlerAndFillWithDefaultsIfMissing(settings); - // init style - StyleController styleController; - styleController.init(engine.rootContext()); - // init main app + /**************************************************************************** + * init style + * Sets default sizes for ui elements. The element size is scaled according + * to the device's display size. + ****************************************************************************/ + StyleHandling styleHandler; + styleHandler.init(engine.rootContext()); + + /**************************************************************************** + * init main app + ****************************************************************************/ NavigationController navController; navController.setContext(engine.rootContext()); navController.init(settingsHandler->getRootPath()); navController.setUiProfile(settingsHandler->getProfile()); + /**************************************************************************** + * init energy saver + * Prevents sleep on android devices and shuts down other device if inactive + * (no music or mouse events) for a certain time intervall + ****************************************************************************/ if(settingsHandler->isEnergySaverEnabled()){ /* install MouseEventSpy and energy saver used for auto shut down of device * if not used for a predefined time. @@ -60,9 +80,13 @@ int main(int argc, char *argv[]) EnergySaver::init(settingsHandler->getEnergySaverTimeout(), settingsHandler->getShutdownScript()); QObject::connect(MouseEventSpy::instance(), &MouseEventSpy::mouseEventDetected, EnergySaver::instance(), &EnergySaver::restartTimer); + } else { + EnergySaver::init(); } - // load GUI + /**************************************************************************** + * load view + ****************************************************************************/ engine.load(QUrl("qrc:/main.qml")); return app.exec(); diff --git a/LenaPi/resources/icon.jpeg b/LenaPi/resources/icon.jpeg new file mode 100644 index 0000000..42118e0 Binary files /dev/null and b/LenaPi/resources/icon.jpeg differ