diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 081349d..5701d2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,63 +1,64 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${QT_INCLUDE_DIR} ) ############ rsibreak ######################################################## # source files needed set(rsibreak_sources slideshoweffect.cpp popupeffect.cpp grayeffect.cpp passivepopup.cpp rsidock.cpp setup.cpp setupgeneral.cpp setuptiming.cpp setupnotifications.cpp rsiwidget.cpp rsirelaxpopup.cpp setupmaximized.cpp rsistatwidget.cpp rsistats.cpp rsitimer.cpp rsitimercounter.cpp rsiglobals.cpp rsistatitem.cpp breakbase.cpp plasmaeffect.cpp breakcontrol.cpp rsiidletime.cpp +notificator.cpp ) QT5_ADD_DBUS_ADAPTOR( rsibreak_sources org.rsibreak.rsiwidget.xml rsiwidget.h RSIObject ) # compilation add_library(rsibreak_lib STATIC ${rsibreak_sources}) add_executable(rsibreak main.cpp) # linking target_link_libraries(rsibreak_lib KF5::ConfigCore KF5::ConfigWidgets KF5::Crash KF5::DBusAddons KF5::IconThemes KF5::I18n KF5::IdleTime KF5::Notifications KF5::NotifyConfig KF5::TextWidgets KF5::XmlGui KF5::WindowSystem Qt5::DBus ) target_link_libraries(rsibreak rsibreak_lib) # install install( TARGETS rsibreak ${INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS rsibreak.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES rsibreak.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) install( FILES org.rsibreak.rsiwidget.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) install( FILES rsibreak_autostart.desktop DESTINATION ${AUTOSTART_INSTALL_DIR} ) diff --git a/src/notificator.cpp b/src/notificator.cpp new file mode 100644 index 0000000..b9f5190 --- /dev/null +++ b/src/notificator.cpp @@ -0,0 +1,56 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "notificator.h" + +#include +#include +#include + +void Notificator::onShortTimerReset() +{ + KNotification::event( "short timer reset", + i18n( "Timer for the short break has now been reset" ), + KIconLoader::global()->loadIcon( "rsibreak0", KIconLoader::Desktop ) ); + +} + +void Notificator::onTimersReset() +{ + KNotification::event( "timers reset", + i18n( "The timers have now been reset" ), + KIconLoader::global()->loadIcon( "rsibreak0", KIconLoader::Desktop ) ); +} + +void Notificator::onStartLongBreak() +{ + KNotification::event( "start long break", i18n( "Start of a long break" ) ); +} + +void Notificator::onEndLongBreak() +{ + KNotification::event( "end long break", i18n( "End of a long break" ) ); +} + +void Notificator::onStartShortBreak() +{ + KNotification::event( "start short break", i18n( "Start of a short break" ) ); +} + +void Notificator::onEndShortBreak() +{ + KNotification::event( "end short break", i18n( "End of a short break" ) ); +} diff --git a/src/notificator.h b/src/notificator.h new file mode 100644 index 0000000..3c9e253 --- /dev/null +++ b/src/notificator.h @@ -0,0 +1,42 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef RSIBREAK_NOTIFICATOR_H +#define RSIBREAK_NOTIFICATOR_H + +#include +#include + +class Notificator : public QObject +{ + Q_OBJECT + +public slots: + + void onShortTimerReset(); + + void onTimersReset(); + + void onStartLongBreak(); + + void onEndLongBreak(); + + void onStartShortBreak(); + + void onEndShortBreak(); +}; + +#endif //RSIBREAK_NOTIFICATOR_H diff --git a/src/rsiglobals.cpp b/src/rsiglobals.cpp index d4272f7..487f3a7 100644 --- a/src/rsiglobals.cpp +++ b/src/rsiglobals.cpp @@ -1,126 +1,112 @@ /* Copyright (C) 2006,2010 Tom Albers Copyright (C) 2010 Juan Luis Baptiste This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "rsiglobals.h" #include #include #include #include #include #include #include #include "rsistats.h" RSIGlobals *RSIGlobals::m_instance = 0; RSIStats *RSIGlobals::m_stats = 0; RSIGlobals::RSIGlobals( QObject *parent ) : QObject( parent ) { resetUsage(); slotReadConfig(); } RSIGlobals::~RSIGlobals() { delete m_stats; m_stats = 0L; } RSIGlobals *RSIGlobals::instance() { if ( !m_instance ) { m_instance = new RSIGlobals(); m_stats = new RSIStats(); } return m_instance; } QString RSIGlobals::formatSeconds( const int seconds ) { return m_format.formatSpelloutDuration( seconds * 1000 ); } void RSIGlobals::slotReadConfig() { KConfigGroup config = KSharedConfig::openConfig()->group( "General Settings" ); m_intervals.resize(INTERVAL_COUNT); m_intervals[TINY_BREAK_INTERVAL] = config.readEntry( "TinyInterval", 10 ) * 60; m_intervals[TINY_BREAK_DURATION] = config.readEntry( "TinyDuration", 20 ); m_intervals[TINY_BREAK_THRESHOLD] = config.readEntry( "TinyThreshold", 20 ); m_intervals[BIG_BREAK_INTERVAL] = config.readEntry( "BigInterval", 60 ) * 60; m_intervals[BIG_BREAK_DURATION] = config.readEntry( "BigDuration", 1 ) * 60; m_intervals[BIG_BREAK_THRESHOLD] = config.readEntry( "BigThreshold", 1 ) * 60; m_intervals[POSTPONE_BREAK_INTERVAL] = config.readEntry( "PostponeBreakDuration", 5 ) * 60; m_intervals[PATIENCE_INTERVAL] = config.readEntry( "Patience", 30 ); if ( config.readEntry( "DEBUG", 0 ) > 0 ) { qDebug() << "Debug mode activated"; m_intervals[TINY_BREAK_INTERVAL] = m_intervals[TINY_BREAK_INTERVAL] / 60; m_intervals[BIG_BREAK_INTERVAL] = m_intervals[BIG_BREAK_INTERVAL] / 60; m_intervals[BIG_BREAK_DURATION] = m_intervals[BIG_BREAK_DURATION] / 60; m_intervals[POSTPONE_BREAK_INTERVAL] = m_intervals[POSTPONE_BREAK_INTERVAL] / 60; } } QColor RSIGlobals::getTinyBreakColor( int secsToBreak ) const { int minimized = m_intervals[TINY_BREAK_INTERVAL]; double v = 100 * secsToBreak / ( double )minimized; v = v > 100 ? 100 : v; v = v < 0 ? 0 : v; return QColor(( int )( 255 - 2.55 * v ), ( int )( 1.60 * v ), 0 ); } QColor RSIGlobals::getBigBreakColor( int secsToBreak ) const { int minimized = m_intervals[BIG_BREAK_INTERVAL]; double v = 100 * secsToBreak / ( double )minimized; v = v > 100 ? 100 : v; v = v < 0 ? 0 : v; return QColor(( int )( 255 - 2.55 * v ), ( int )( 1.60 * v ), 0 ); } void RSIGlobals::resetUsage() { m_usageArray.fill( false, 60 * 60 * 24 ); } - -void RSIGlobals::NotifyBreak( bool start, bool big ) -{ - if ( start ) - big ? KNotification::event( "start long break", - i18n( "Start of a long break" ) ) - : KNotification::event( "start short break", - i18n( "Start of a short break" ) ); - else - big ? KNotification::event( "end long break", - i18n( "End of a long break" ) ) - : KNotification::event( "end short break", - i18n( "End of a short break" ) ); -} diff --git a/src/rsitimer.cpp b/src/rsitimer.cpp index f2a1071..43a4dc4 100644 --- a/src/rsitimer.cpp +++ b/src/rsitimer.cpp @@ -1,309 +1,318 @@ /* Copyright (C) 2005-2006,2008-2010 Tom Albers Copyright (C) 2005-2006 Bram Schoenmakers Copyright (C) 2010 Juan Luis Baptiste The parts for idle detection is based on kdepim's karm idletimedetector.cpp/.h This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "rsitimer.h" #include #include #include #include #include +#include "rsiglobals.h" #include "rsistats.h" RSITimer::RSITimer( QObject *parent ) : QThread( parent ) , m_idleTimeInstance( new RSIIdleTimeImpl() ) , m_intervals( RSIGlobals::instance()->intervals() ) , m_state ( TimerState::Monitoring ) { updateConfig( true ); } RSITimer::RSITimer( std::unique_ptr &&_idleTime, const QVector _intervals, const bool _usePopup, const bool _useIdleTimers ) : QThread( nullptr ) , m_idleTimeInstance( std::move(_idleTime) ) , m_usePopup( _usePopup ) , m_useIdleTimers( _useIdleTimers ) , m_intervals( _intervals ) , m_state( TimerState::Monitoring ) { createTimers(); } void RSITimer::createTimers() { int bigThreshold = m_useIdleTimers ? m_intervals[BIG_BREAK_THRESHOLD] : INT_MAX; int tinyThreshold = m_useIdleTimers ? m_intervals[TINY_BREAK_THRESHOLD] : INT_MAX; m_bigBreakCounter = std::unique_ptr { new RSITimerCounter( m_intervals[BIG_BREAK_INTERVAL], m_intervals[BIG_BREAK_DURATION], bigThreshold ) }; m_tinyBreakCounter = std::unique_ptr { new RSITimerCounter( m_intervals[TINY_BREAK_INTERVAL], m_intervals[TINY_BREAK_DURATION], tinyThreshold ) }; } void RSITimer::run() { QTimer timer; connect( &timer, &QTimer::timeout, this, &RSITimer::timeout ); timer.setTimerType( Qt::TimerType::CoarseTimer ); timer.start( 1000 ); exec(); // start event loop to make timers work. } void RSITimer::hibernationDetector( const int totalIdle ) { // poor mans hibernation detector.... static QDateTime last = QDateTime::currentDateTime(); QDateTime current = QDateTime::currentDateTime(); if ( last.secsTo( current ) > 60 ) { qDebug() << "Not been checking idleTime for more than 60 seconds, " << "assuming the computer hibernated, resetting timers" << "Last: " << last << "Current: " << current << "Idle, s: " << totalIdle; resetAfterBreak(); } last = current; } int RSITimer::idleTime() { int totalIdle = m_idleTimeInstance->getIdleTime() / 1000; hibernationDetector( totalIdle ); // TODO Find a modern-desktop way to check if the screensaver is inhibited // and disable the timer because we assume you're doing for example a presentation and // don't want rsibreak to annoy you return totalIdle; } void RSITimer::doBreakNow( const int breakTime, const bool nextBreakIsBig ) { m_state = TimerState::Resting; m_pauseCounter = std::unique_ptr { new RSITimerCounter( breakTime, breakTime, INT_MAX ) }; m_popupCounter = nullptr; - RSIGlobals::instance()->NotifyBreak( true, nextBreakIsBig ); + if ( nextBreakIsBig ) { + emit startLongBreak(); + } else { + emit startShortBreak(); + } emit updateWidget( breakTime ); emit breakNow(); } void RSITimer::resetAfterBreak() { m_state = TimerState::Monitoring; m_pauseCounter = nullptr; m_popupCounter = nullptr; defaultUpdateToolTip(); emit updateIdleAvg( 0.0 ); emit relax( -1, false ); emit minimize(); - RSIGlobals::instance()->NotifyBreak( false, m_bigBreakCounter->isReset() ); + if ( m_bigBreakCounter->isReset() ) { + emit endLongBreak(); + } else { + emit endShortBreak(); + } } // -------------------------- SLOTS ------------------------// void RSITimer::slotStart() { m_state = TimerState::Monitoring; } void RSITimer::slotStop() { m_state = TimerState::Suspended; emit updateIdleAvg( 0.0 ); emit updateToolTip( 0, 0 ); } void RSITimer::slotSuspended( bool suspend ) { suspend ? slotStop() : slotStart(); } void RSITimer::slotLock() { resetAfterBreak(); } void RSITimer::skipBreak() { if ( m_bigBreakCounter->isReset() ) { RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS_SKIPPED ); emit bigBreakSkipped(); } else { RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS_SKIPPED ); emit tinyBreakSkipped(); } resetAfterBreak(); } void RSITimer::postponeBreak() { if ( m_bigBreakCounter->isReset() ) { m_bigBreakCounter->postpone( m_intervals[POSTPONE_BREAK_INTERVAL] ); RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS_POSTPONED ); } else { m_tinyBreakCounter->postpone( m_intervals[POSTPONE_BREAK_INTERVAL] ); RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS_POSTPONED ); } resetAfterBreak(); } void RSITimer::updateConfig( bool doRestart ) { KConfigGroup popupConfig = KSharedConfig::openConfig()->group( "Popup Settings" ); m_usePopup = popupConfig.readEntry( "UsePopup", true ); bool oldUseIdleTimers = m_useIdleTimers; KConfigGroup generalConfig = KSharedConfig::openConfig()->group( "General Settings" ); m_useIdleTimers = !( generalConfig.readEntry( "UseNoIdleTimer", false ) ); doRestart = doRestart || ( oldUseIdleTimers != m_useIdleTimers ); const QVector oldIntervals = m_intervals; m_intervals = RSIGlobals::instance()->intervals(); doRestart = doRestart || ( m_intervals != oldIntervals ); if ( doRestart ) { qDebug() << "Timeout parameters have changed, counters were reset."; createTimers(); } } // ----------------------------- EVENTS -----------------------// void RSITimer::timeout() { // Don't change the tray icon when suspended, or evaluate a possible break. if ( m_state == TimerState::Suspended ) { return; } const int idleSeconds = idleTime(); // idleSeconds == 0 means activity RSIGlobals::instance()->stats()->increaseStat( TOTAL_TIME ); RSIGlobals::instance()->stats()->setStat( CURRENT_IDLE_TIME, idleSeconds ); if ( idleSeconds == 0 ) { RSIGlobals::instance()->stats()->increaseStat( ACTIVITY ); } else { RSIGlobals::instance()->stats()->setStat( MAX_IDLENESS, idleSeconds, true ); } switch ( m_state ) { case TimerState::Monitoring: { // This is a weird thing to track as now when user was away, they will get back to zero counters, // not to an arbitrary time elapsed since last "idleness-skip-break". bool bigWasReset = m_bigBreakCounter->isReset(); bool tinyWasReset = m_tinyBreakCounter->isReset(); int breakTime = std::max( m_bigBreakCounter->tick( idleSeconds ), m_tinyBreakCounter->tick( idleSeconds ) ); if ( breakTime > 0 ) { suggestBreak( breakTime ); } else { // Not a time for break yet, but if one of the counters got reset, that means we were idle enough to skip. if ( !bigWasReset && m_bigBreakCounter->isReset() ) { RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS ); RSIGlobals::instance()->stats()->increaseStat( IDLENESS_CAUSED_SKIP_BIG ); } if ( !tinyWasReset && m_tinyBreakCounter->isReset() ) { RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS ); RSIGlobals::instance()->stats()->increaseStat( IDLENESS_CAUSED_SKIP_TINY ); } } const double value = 100.0 - ( ( m_tinyBreakCounter->counterLeft() / ( double ) m_intervals[TINY_BREAK_INTERVAL] ) * 100.0 ); emit updateIdleAvg( value ); break; } case TimerState::Suggesting: { // Using popupCounter to count down our patience here. int breakTime = m_popupCounter->tick( idleSeconds ); if ( breakTime > 0 ) { // User kept working throw the suggestion timeout. Well, their loss. emit relax( -1, false ); breakTime = m_pauseCounter->counterLeft(); doBreakNow( breakTime, false ); break; } int inverseTick = ( idleSeconds == 0 ) ? 1 : 0; // inverting as we account idle seconds here. breakTime = m_pauseCounter->tick( inverseTick ); if ( breakTime > 0 ) { // User has waited out the pause, back to monitoring. resetAfterBreak(); break; } emit relax( m_pauseCounter->counterLeft(), false ); emit updateWidget( m_pauseCounter->counterLeft() ); break; } case TimerState::Resting: { int inverseTick = ( idleSeconds == 0 ) ? 1 : 0; // inverting as we account idle seconds here. int breakTime = m_pauseCounter->tick( inverseTick ); if ( breakTime > 0 ) { resetAfterBreak(); } else { emit updateWidget( m_pauseCounter->counterLeft() ); } break; } default: qDebug() << "Reached unexpected state"; } defaultUpdateToolTip(); } void RSITimer::suggestBreak( const int breakTime ) { if ( m_bigBreakCounter->isReset() ) { RSIGlobals::instance()->stats()->increaseStat( BIG_BREAKS ); RSIGlobals::instance()->stats()->setStat( LAST_BIG_BREAK, QVariant( QDateTime::currentDateTime() ) ); } else { RSIGlobals::instance()->stats()->increaseStat( TINY_BREAKS ); RSIGlobals::instance()->stats()->setStat( LAST_TINY_BREAK, QVariant( QDateTime::currentDateTime() ) ); } bool nextOneIsBig = m_bigBreakCounter->counterLeft() <= m_tinyBreakCounter->getDelayTicks(); if ( !m_usePopup ) { doBreakNow( breakTime, nextOneIsBig ); return; } m_state = TimerState::Suggesting; // When pause is longer than patience, we need to reset patience timer so that we don't flip to break now in // mid-pause. Patience / 2 is a good alternative to it by extending patience if user was idle long enough. m_popupCounter = std::unique_ptr { new RSITimerCounter( m_intervals[PATIENCE_INTERVAL], breakTime, m_intervals[PATIENCE_INTERVAL] / 2 ) }; // Threshold of one means the timer is reset on every non-zero tick. m_pauseCounter = std::unique_ptr { new RSITimerCounter( breakTime, breakTime, 1 ) }; emit relax( breakTime, nextOneIsBig ); } void RSITimer::defaultUpdateToolTip() { emit updateToolTip( m_tinyBreakCounter->counterLeft(), m_bigBreakCounter->counterLeft() ); } diff --git a/src/rsitimer.h b/src/rsitimer.h index 1054872..2e0029f 100644 --- a/src/rsitimer.h +++ b/src/rsitimer.h @@ -1,206 +1,212 @@ /* Copyright (C) 2005-2006,2008-2010 Tom Albers Copyright (C) 2005-2006 Bram Schoenmakers Copyright (C) 2010 Juan Luis Baptiste This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RSITimer_H #define RSITimer_H #include +#include #include -#include "rsiglobals.h" #include "rsitimercounter.h" #include "rsiidletime.h" /** * @class RSITimer * This class controls the timings and arranges the maximizing * and minimizing of the widget. * @author Tom Albers */ class RSITimer : public QThread { Q_OBJECT friend class RSITimerTest; public: /** * Constructor * @param parent Parent Widget * @param name Name */ explicit RSITimer( QObject *parent = 0 ); // Check whether the timer is suspended. bool isSuspended() const { return m_state == TimerState::Suspended; } int tinyLeft() const { return m_tinyBreakCounter->counterLeft(); }; int bigLeft() const { return m_bigBreakCounter->counterLeft(); }; public slots: + /** Reads the configuration and restarts the timer with slotRestart. */ void updateConfig( bool doRestart = false ); /** Stops the timer activity. This does not imply resetting counters. */ void slotStop(); /** Called when the user suspends RSIBreak from the docker. @param suspend If true the timer will suspend, if false the timer will unsuspend. */ void slotSuspended( bool suspend ); /** Prepares the timer so that it can start/continue. */ void slotStart(); /** Called when user locks the screen for pause. Resets current timers if currently suggesting. */ void slotLock(); /** When the user presses the Skip button during a break, this function will be called. It will act like a break has just passed. */ void skipBreak(); /** When the user presses the postpone break button during a break, this function will be called. It will postpone the break for the configured amount of seconds. */ void postponeBreak(); /** Queries X how many seconds the user has been idle. A value of 0 means there was activity during the last second. @returns The amount of seconds of idling. */ int idleTime(); private slots: /** The pumping heart of the timer. This will evaluate user's activity and decide what to do (wait, popup a relax notification or a fullscreen break. */ virtual void timeout(); signals: /** Enforce a fullscreen big break. */ void breakNow(); /** Update counters in tooltip. @param tinyLeft If <=0 a tiny break is active, else it defines how much time is left until the next tiny break. @param bigLeft If <=0 a big break is active, else it defines how much time is left until the next big break. */ void updateToolTip( const int tinyLeft, const int bigLeft ); /** Update the time shown on the fullscreen widget. @param secondsLeft Shows the user how many seconds are remaining. */ void updateWidget( int secondsLeft ); /** Update the systray icon. @param v How much time has passed until a tiny break (relative) Varies from 0 to 100. */ void updateIdleAvg( double v ); /** A request to minimize the fullscreen widget, for example when the break is over. @param newImage Load a new image */ void minimize(); /** Pop up a relax notification to the user for @p sec seconds. @param sec The amount of seconds the user should relax to make the popup disappear. A value of -1 will hide the relax popup. @param nextBreakIsBig True if the break after the next break is a big break. We can warn the user in advance. */ void relax( int sec, bool nextBreakIsBig ); /** Indicates a tinyBreak is skipped because user was enough idle */ void tinyBreakSkipped(); /** Indicates a bigBreak is skipped because user was enough idle */ void bigBreakSkipped(); + void startLongBreak(); + void endLongBreak(); + void startShortBreak(); + void endShortBreak(); + private: std::unique_ptr m_idleTimeInstance; bool m_usePopup; bool m_useIdleTimers; QVector m_intervals; enum class TimerState { Suspended = 0, // user has suspended either via dbus or tray. Monitoring, // normal cycle, waiting for break to trigger. Suggesting, // politely suggest to take a break with some patience. Resting // suggestion ignored, waiting out the break. } m_state; std::unique_ptr m_bigBreakCounter; std::unique_ptr m_tinyBreakCounter; std::unique_ptr m_pauseCounter; std::unique_ptr m_popupCounter; void hibernationDetector( const int totalIdle ); void suggestBreak( const int time ); void defaultUpdateToolTip(); void createTimers(); // This function is called when a break has passed. void resetAfterBreak(); // Start this thread. void run(); /** Some internal preparations for a fullscreen break window. @param breakTime The amount of seconds to break. @param nextBreakIsBig Whether the next break will be big. */ void doBreakNow( const int breakTime, const bool nextBreakIsBig ); // Constructor for tests. RSITimer( std::unique_ptr &&_idleTime, const QVector _intervals, const bool _usePopup, const bool _useIdleTimers ); }; #endif diff --git a/src/rsiwidget.cpp b/src/rsiwidget.cpp index 038c25a..caf688f 100644 --- a/src/rsiwidget.cpp +++ b/src/rsiwidget.cpp @@ -1,277 +1,277 @@ /* Copyright (C) 2005-2006,2009-2010 Tom Albers Copyright (C) 2006 Bram Schoenmakers Copyright (C) 2010 Juan Luis Baptiste This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "rsiwidget.h" #include "rsiwidgetadaptor.h" #include "grayeffect.h" #include "popupeffect.h" #include "plasmaeffect.h" #include "slideshoweffect.h" #include "rsitimer.h" #include "rsidock.h" #include "rsirelaxpopup.h" #include "rsiglobals.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include RSIObject::RSIObject( QWidget *parent ) : QObject( parent ) , m_timer(nullptr), m_effect( 0 ) , m_useImages( false ), m_usePlasma( false ), m_usePlasmaRO( false ) { // Keep these 2 lines _above_ the messagebox, so the text actually is right. m_tray = new RSIDock( this ); m_tray->setIconByName( "rsibreak0" ); new RsiwidgetAdaptor( this ); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject( "/rsibreak", this ); m_relaxpopup = new RSIRelaxPopup( 0 ); connect(m_relaxpopup, &RSIRelaxPopup::lock, this, &RSIObject::slotLock); connect(m_tray, &RSIDock::configChanged, this, &RSIObject::readConfig); connect(m_tray, &RSIDock::configChanged, RSIGlobals::instance(), &RSIGlobals::slotReadConfig ); connect(m_tray, &RSIDock::configChanged, m_relaxpopup, &RSIRelaxPopup::slotReadConfig); connect(m_tray, &RSIDock::suspend, m_relaxpopup, &RSIRelaxPopup::setSuspended); qsrand( time( NULL ) ); readConfig(); setIcon( 0 ); QTimer::singleShot( 2000, this, &RSIObject::slotWelcome ); } RSIObject::~RSIObject() { delete m_effect; delete RSIGlobals::instance(); if (m_timer != nullptr) { m_timer->quit(); m_timer->wait(); delete m_timer; } } void RSIObject::slotWelcome() { if ( KMessageBox::shouldBeShownContinue( "dont_show_welcome_again_for_001" ) ) { KMessageBox::information( 0, i18n( "

Welcome to RSIBreak

\n

" "In your tray you can now see RSIBreak.

\n" ) + i18n( "

When you right-click on that you will see a menu, from which " "you can go to the configuration for example.

\n

When you want to " "know when the next break is, hover over the icon.

\n

Use RSIBreak " "wisely.

" ), i18n( "Welcome" ), "dont_show_welcome_again_for_001" ); } } void RSIObject::minimize() { m_effect->deactivate(); } void RSIObject::maximize() { m_effect->activate(); } void RSIObject::slotLock() { m_effect->deactivate(); m_timer->slotLock(); QDBusInterface lock( "org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver" ); lock.call( "Lock" ); } void RSIObject::setCounters( int timeleft ) { if ( timeleft > 0 ) { m_effect->setLabel( KFormat().formatSpelloutDuration( timeleft * 1000 ) ); } else if ( m_timer->isSuspended() ) { m_effect->setLabel( i18n( "Suspended" ) ); } else { m_effect->setLabel( QString() ); } } void RSIObject::updateIdleAvg( double idleAvg ) { if ( idleAvg == 0.0 ) setIcon( 0 ); else if ( idleAvg > 0 && idleAvg < 30 ) setIcon( 1 ); else if ( idleAvg >= 30 && idleAvg < 60 ) setIcon( 2 ); else if ( idleAvg >= 60 && idleAvg < 90 ) setIcon( 3 ); else setIcon( 4 ); } void RSIObject::setIcon( int level ) { QString newIcon = "rsibreak" + ( m_timer->isSuspended() ? QString( "x" ) : QString::number( level ) ); if ( newIcon != m_currentIcon ) { m_tray->setIconByName( newIcon ); m_tray->setToolTipIconByName( newIcon ); m_currentIcon = newIcon; } } // ------------------- Popup for skipping break ------------- // void RSIObject::tinyBreakSkipped() { - KNotification::event( "short timer reset", - i18n( "Timer for the short break has now been reset" ), - KIconLoader::global()->loadIcon( "rsibreak0", KIconLoader::Desktop ) ); + m_notificator.onShortTimerReset(); } void RSIObject::bigBreakSkipped() { - KNotification::event( "timers reset", - i18n( "The timers have now been reset" ), - KIconLoader::global()->loadIcon( "rsibreak0", KIconLoader::Desktop ) ); + m_notificator.onTimersReset(); } //--------------------------- CONFIG ----------------------------// void RSIObject::configureTimer() { if (m_timer != nullptr) { m_timer->updateConfig(); return; } m_timer = new RSITimer(this); connect(m_timer, &RSITimer::breakNow, this, &RSIObject::maximize, Qt::QueuedConnection ); connect(m_timer, &RSITimer::updateWidget, this, &RSIObject::setCounters, Qt::QueuedConnection ); connect(m_timer, &RSITimer::updateToolTip, m_tray, &RSIDock::setCounters, Qt::QueuedConnection ); connect(m_timer, &RSITimer::updateIdleAvg, this, &RSIObject::updateIdleAvg, Qt::QueuedConnection ); connect(m_timer, &RSITimer::minimize, this, &RSIObject::minimize, Qt::QueuedConnection ); connect(m_timer, &RSITimer::relax, m_relaxpopup, &RSIRelaxPopup::relax, Qt::QueuedConnection ); connect(m_timer, &RSITimer::tinyBreakSkipped, this, &RSIObject::tinyBreakSkipped, Qt::QueuedConnection ); connect(m_timer, &RSITimer::bigBreakSkipped, this, &RSIObject::bigBreakSkipped, Qt::QueuedConnection ); + connect(m_timer, &RSITimer::startLongBreak, &m_notificator, &Notificator::onStartLongBreak ); + connect(m_timer, &RSITimer::endLongBreak, &m_notificator, &Notificator::onEndLongBreak ); + connect(m_timer, &RSITimer::startShortBreak, &m_notificator, &Notificator::onStartShortBreak ); + connect(m_timer, &RSITimer::endShortBreak, &m_notificator, &Notificator::onEndShortBreak ); connect(m_tray, &RSIDock::configChanged, m_timer, &RSITimer::updateConfig); connect(m_tray, &RSIDock::dialogEntered, m_timer, &RSITimer::slotStop); connect(m_tray, &RSIDock::dialogLeft, m_timer, &RSITimer::slotStart); connect(m_tray, &RSIDock::suspend, m_timer, &RSITimer::slotSuspended); connect(m_relaxpopup, &RSIRelaxPopup::skip, m_timer, &RSITimer::skipBreak); connect(m_relaxpopup, &RSIRelaxPopup::postpone, m_timer, &RSITimer::postponeBreak); m_timer->start(); } void RSIObject::readConfig() { KConfigGroup config = KSharedConfig::openConfig()->group( "General Settings" ); m_relaxpopup->setSkipButtonHidden( config.readEntry( "HideMinimizeButton", false ) ); m_relaxpopup->setLockButtonHidden( config.readEntry( "HideLockButton", false ) ); m_relaxpopup->setPostponeButtonHidden( config.readEntry( "HidePostponeButton", false ) ); m_usePlasma = config.readEntry( "UsePlasma", false ); m_usePlasmaRO = config.readEntry( "UsePlasmaReadOnly", false ); m_useImages = config.readEntry( "ShowImages", false ); int slideInterval = config.readEntry( "SlideInterval", 10 ); bool recursive = config.readEntry( "SearchRecursiveCheck", false ); bool showSmallImages = config.readEntry( "ShowSmallImagesCheck", true ); const bool expandImageToFullScreen = config.readEntry( "ExpandImageToFullScreen", true ); QString path = config.readEntry( "ImageFolder" ); configureTimer(); int effect = config.readEntry( "Effect", 0 ); delete m_effect; switch ( effect ) { case Plasma: { m_effect = new PlasmaEffect( 0 ); m_effect->setReadOnly( m_usePlasmaRO ); break; } case SlideShow: { SlideEffect* slide = new SlideEffect( 0 ); slide->reset( path, recursive, showSmallImages, expandImageToFullScreen, slideInterval ); if ( slide->hasImages() ) m_effect = slide; else { delete slide; m_effect = new GrayEffect( 0 ); } break; } case Popup: { PopupEffect* effect = new PopupEffect( 0 ); m_effect = effect; break; } case SimpleGray: default: { GrayEffect* effect = new GrayEffect( 0 ); effect->setLevel( config.readEntry( "Graylevel", 80 ) ); m_effect = effect; break; } } connect(m_effect, &BreakBase::skip, m_timer, &RSITimer::skipBreak); connect(m_effect, &BreakBase::lock, this, &RSIObject::slotLock); connect(m_effect, &BreakBase::postpone, m_timer, &RSITimer::postponeBreak); m_effect->showMinimize( !config.readEntry( "HideMinimizeButton", false ) ); m_effect->showLock( !config.readEntry( "HideLockButton", false ) ); m_effect->showPostpone( !config.readEntry( "HidePostponeButton", false ) ); m_effect->disableShortcut( config.readEntry( "DisableAccel", false ) ); } void RSIObject::resume() { m_tray->doResume(); } void RSIObject::suspend() { m_tray->doSuspend(); } diff --git a/src/rsiwidget.h b/src/rsiwidget.h index ae3a895..1230a0b 100644 --- a/src/rsiwidget.h +++ b/src/rsiwidget.h @@ -1,117 +1,117 @@ /* Copyright (C) 2005-2006 Tom Albers Copyright (C) 2006 Bram Schoenmakers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RSIWIDGET_H #define RSIWIDGET_H #include "rsitimer.h" +#include "notificator.h" class RSIDock; class RSIRelaxPopup; class BreakBase; -class QLabel; - /** * @class RSIObject * This controls all RSIBreak components * @author Tom Albers */ class RSIObject : public QObject { Q_OBJECT Q_CLASSINFO( "D-Bus Interface", "org.rsibreak.rsiwidget" ) public: enum Effects { SimpleGray = 0, Plasma, SlideShow, Popup }; /** * Constructor * @param parent Parent Widget * @param name Name */ explicit RSIObject( QWidget *parent = 0 ); /** * Destructor */ ~RSIObject(); /** * Access to the timer */ RSITimer* timer() { return m_timer; }; private slots: void slotWelcome(); void slotLock(); void minimize(); void maximize(); void setCounters( int ); void updateIdleAvg( double ); void readConfig(); void tinyBreakSkipped(); void bigBreakSkipped(); protected: /** Sets appropriate icon in tooltip and docker. */ void setIcon( int ); private: void findImagesInFolder( const QString& folder ); void loadImage(); void configureTimer(); RSIDock* m_tray; RSITimer* m_timer; BreakBase* m_effect; bool m_useImages; bool m_usePlasma; bool m_usePlasmaRO; RSIRelaxPopup* m_relaxpopup; QString m_currentIcon; + Notificator m_notificator; /* Available through D-Bus */ public Q_SLOTS: void resume(); void suspend(); int idleTime() { return timer()->idleTime(); } int tinyLeft() { return timer()->tinyLeft(); } int bigLeft() { return timer()->bigLeft(); } QString currentIcon() { return m_currentIcon; } }; # endif diff --git a/test/rsitimer_test.cpp b/test/rsitimer_test.cpp index 73c88b2..85651ae 100644 --- a/test/rsitimer_test.cpp +++ b/test/rsitimer_test.cpp @@ -1,370 +1,388 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "rsitimer_test.h" +#include "rsiglobals.h" #include "rsitimer.h" static constexpr int RELAX_ENDED_MAGIC_VALUE = -1; RSITimerTest::RSITimerTest( void ) { m_intervals.resize( INTERVAL_COUNT ); m_intervals[TINY_BREAK_INTERVAL] = 15 * 60; m_intervals[TINY_BREAK_DURATION] = 20; m_intervals[TINY_BREAK_THRESHOLD] = 60; m_intervals[BIG_BREAK_INTERVAL] = 60 * 60; m_intervals[BIG_BREAK_DURATION] = 60; m_intervals[BIG_BREAK_THRESHOLD] = 5 * 60; m_intervals[POSTPONE_BREAK_INTERVAL] = 3 * 60; m_intervals[PATIENCE_INTERVAL] = 30; } void RSITimerTest::triggerSimpleTinyBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); + QSignalSpy spyEndShortBreak( &timer, SIGNAL( endShortBreak( void ) ) ); + // Part one, no idleness till small break. QSignalSpy spy1Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy1UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); QCOMPARE( spy1Relax.count(), 1 ); QList spy1RelaxSignals = spy1Relax.takeFirst(); QCOMPARE( spy1RelaxSignals.at( 0 ).toInt(), m_intervals[TINY_BREAK_DURATION] ); QCOMPARE( spy1RelaxSignals.at( 1 ).toBool(), false ); QCOMPARE( spy1UpdateIdleAvg.count(), m_intervals[TINY_BREAK_INTERVAL] ); double lastAvg = 0; for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { QList spy1UpdateIdleAvgSignals = spy1UpdateIdleAvg.takeFirst(); bool ok; double newAvg = spy1UpdateIdleAvgSignals.at( 0 ).toDouble( &ok ); QVERIFY2( ok && ( newAvg >= lastAvg ) && ( newAvg <= 100.0 ), QString( "Unexpected newAvg value: %1, lastAvg: %2" ).arg( newAvg ).arg( lastAvg ).toLatin1() ); } // Part two, obeying and idle as suggested. QSignalSpy spy2Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy2UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); QSignalSpy spy2Minimize( &timer, SIGNAL(minimize()) ); for ( int i = 0; i < m_intervals[TINY_BREAK_DURATION]; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QCOMPARE( spy2Minimize.count(), 1 ); QCOMPARE( spy2Relax.count(), m_intervals[TINY_BREAK_DURATION] ); for ( int i = 1; i < m_intervals[TINY_BREAK_DURATION]; i++ ) { QList spy2RelaxSignals = spy2Relax.takeFirst(); QCOMPARE( spy2RelaxSignals.at( 0 ).toInt(), m_intervals[TINY_BREAK_DURATION] - i ); } QList spy2RelaxSignals = spy2Relax.takeFirst(); // The last one is special. QCOMPARE( spy2RelaxSignals.at( 0 ).toInt(), RELAX_ENDED_MAGIC_VALUE ); + QCOMPARE( spyEndShortBreak.count(), 1 ); } void RSITimerTest::triggerComplexTinyBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); int part1 = 10; // Non-idle int part2 = 40; // Idle int part3 = m_intervals[TINY_BREAK_INTERVAL] - part1 - part2; // The rest non-idle. // Part 1, no idleness. QSignalSpy spy1Relax( &timer, SIGNAL( relax( int, bool ) ) ); QSignalSpy spy1UpdateIdleAvg( &timer, SIGNAL( updateIdleAvg( double ) ) ); idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < part1; i++ ) { timer.timeout(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); } QCOMPARE( spy1Relax.count(), 0 ); QCOMPARE( spy1UpdateIdleAvg.count(), part1 ); // Part 2, idle for a while. QSignalSpy spy2Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy2UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); for ( int i = 0; i < part2; i++ ) { idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); } QCOMPARE( spy2Relax.count(), 0 ); QCOMPARE( spy2UpdateIdleAvg.count(), part2 ); // Part 3, non-idle till break. QSignalSpy spy3Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy3UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); for ( int i = 0; i < part3; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); idle_time_ptr->setIdleTime( 0 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); QCOMPARE( spy3Relax.count(), 1 ); } void RSITimerTest::testSuspended() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); timer.slotStop(); QCOMPARE( timer.m_state, RSITimer::TimerState::Suspended ); QSignalSpy spy1Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy1UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); // Not idle for long enough to have a break. idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { timer.timeout(); QCOMPARE( timer.m_state, RSITimer::TimerState::Suspended ); } QCOMPARE( spy1Relax.count(), 0 ); QCOMPARE( spy1UpdateIdleAvg.count(), 0 ); timer.slotStart(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); } void RSITimerTest::triggerSimpleBigBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); int tinyBreaks = m_intervals[BIG_BREAK_INTERVAL] / ( m_intervals[TINY_BREAK_INTERVAL] + m_intervals[PATIENCE_INTERVAL] + m_intervals[TINY_BREAK_DURATION] ); // We don't tick big pause timer during tiny breaks and patience, so it will actually happen later. int ticks = m_intervals[BIG_BREAK_INTERVAL] + tinyBreaks * ( m_intervals[PATIENCE_INTERVAL] + m_intervals[TINY_BREAK_DURATION] ); + QSignalSpy spyEndLongBreak( &timer, SIGNAL( endLongBreak( void ) ) ); + // Part one, no idleness till big break. QSignalSpy spy1Relax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spy1UpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < ticks; i++ ) { timer.timeout(); } // Number of relax updates during N tiny breaks, plus one for the actual big break. int relaxCountExp = tinyBreaks * ( 2 + m_intervals[PATIENCE_INTERVAL] ) + 1; QCOMPARE( spy1Relax.count(), relaxCountExp ); QVERIFY2( spy1UpdateIdleAvg.count() >= m_intervals[BIG_BREAK_INTERVAL], "Failed to update the indicator regularly." ); // Part two, making the big break. QSignalSpy spy2Relax( &timer, SIGNAL(relax(int,bool)) ); for ( int i = 0; i < m_intervals[BIG_BREAK_DURATION]; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QCOMPARE( spy2Relax.count(), m_intervals[BIG_BREAK_DURATION] ); + QCOMPARE( spyEndLongBreak.count(), 1 ); } void RSITimerTest::postponeBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); // Not idle for long enough to have a break. idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); QSignalSpy spyRelax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spyMinimize( &timer, SIGNAL(minimize()) ); timer.postponeBreak(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QList spyRelaxSignals = spyRelax.takeFirst(); QCOMPARE( spyRelaxSignals.at( 0 ).toInt(), RELAX_ENDED_MAGIC_VALUE ); QCOMPARE( spyMinimize.count(), 1 ); // Waiting out postpone interval to confirm the break happens. for ( int i = 0; i < m_intervals[POSTPONE_BREAK_INTERVAL]; i++ ) { timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); } void RSITimerTest::screenLock() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); // Not idle for long enough to have a break. idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); QSignalSpy spyRelax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spyMinimize( &timer, SIGNAL(minimize()) ); timer.slotLock(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QList spyRelaxSignals = spyRelax.takeFirst(); QCOMPARE( spyRelaxSignals.at( 0 ).toInt(), RELAX_ENDED_MAGIC_VALUE ); QCOMPARE( spyMinimize.count(), 1 ); QVERIFY2( timer.m_bigBreakCounter->counterLeft() < m_intervals[BIG_BREAK_INTERVAL], "Big break counter was reset on screen lock when it should have not." ); } void RSITimerTest::skipBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, true ); // Not idle for long enough to have a break. idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); QSignalSpy spyRelax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spyMinimize( &timer, SIGNAL(minimize()) ); timer.skipBreak(); QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QList spyRelaxSignals = spyRelax.takeFirst(); QCOMPARE( spyRelaxSignals.at( 0 ).toInt(), RELAX_ENDED_MAGIC_VALUE ); QCOMPARE( spyMinimize.count(), 1 ); QVERIFY2( timer.m_bigBreakCounter->counterLeft() < m_intervals[BIG_BREAK_INTERVAL], "Big break counter was reset on skip break when it should have not." ); } void RSITimerTest::noPopupBreak() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, false, true ); + QSignalSpy spyStartShortBreak( &timer, SIGNAL( startShortBreak( void ) ) ); + QSignalSpy spyEndShortBreak( &timer, SIGNAL( endShortBreak( void ) ) ); + // Part one, no idleness till small break. QSignalSpy spy1BreakNow( &timer, SIGNAL(breakNow()) ); QSignalSpy spy1UpdateWidget( &timer, SIGNAL(updateWidget(int)) ); idle_time_ptr->setIdleTime( 0 ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); timer.timeout(); } // Popup is disabled so straight to breaking. + QCOMPARE( spyStartShortBreak.count(), 1 ); QCOMPARE( timer.m_state, RSITimer::TimerState::Resting ); QCOMPARE( spy1BreakNow.count(), 1 ); QList spy1UpdateWidgetSignals = spy1UpdateWidget.takeFirst(); QCOMPARE( spy1UpdateWidgetSignals.at( 0 ).toInt(), m_intervals[TINY_BREAK_DURATION] ); // Part two, waiting out break. QSignalSpy spy2UpdateWidget( &timer, SIGNAL(updateWidget(int)) ); QSignalSpy spy2Minimize( &timer, SIGNAL(minimize()) ); for ( int i = 0; i < m_intervals[TINY_BREAK_DURATION]; i++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Resting ); idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); QCOMPARE( spy2Minimize.count(), 1 ); QCOMPARE( spy2UpdateWidget.count(), m_intervals[TINY_BREAK_DURATION] - 1 ); for ( int i = 1; i < m_intervals[TINY_BREAK_DURATION]; i++ ) { QList spy2UpdateWidgetSignals = spy2UpdateWidget.takeFirst(); QCOMPARE( spy2UpdateWidgetSignals.at( 0 ).toInt(), m_intervals[TINY_BREAK_DURATION] - i ); } + QCOMPARE( spyEndShortBreak.count(), 1 ); } void RSITimerTest::regularBreaks() { std::unique_ptr idle_time( new RSIIdleTimeFake() ); RSIIdleTimeFake* idle_time_ptr = idle_time.get(); RSITimer timer( std::move( idle_time ), m_intervals, true, false ); + QSignalSpy spyEndShortBreak( &timer, SIGNAL( endShortBreak( void ) ) ); + QSignalSpy spyEndLongBreak( &timer, SIGNAL( endLongBreak( void ) ) ); + int tinyBreaks = m_intervals[BIG_BREAK_INTERVAL] / ( m_intervals[TINY_BREAK_INTERVAL] + m_intervals[PATIENCE_INTERVAL] + m_intervals[TINY_BREAK_DURATION] ); int tick = 0; for ( int j = 0; j < tinyBreaks; j++ ) { // Tiny break, mix of activity and idleness till small break. QSignalSpy spyRelax( &timer, SIGNAL(relax(int,bool)) ); QSignalSpy spyUpdateIdleAvg( &timer, SIGNAL(updateIdleAvg(double)) ); for ( int i = 0; i < m_intervals[TINY_BREAK_INTERVAL]; ++i, ++tick ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); if ( i % 2 == 0 ) { idle_time_ptr->setIdleTime( 0 ); } else { idle_time_ptr->setIdleTime( 1000 ); } timer.timeout(); } for ( int i = 0; i < m_intervals[TINY_BREAK_DURATION]; ++i, ++tick ) { // No activity during break -- obeying. QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); } // Expected ticks till big break, accounting for pauses. int ticks = m_intervals[BIG_BREAK_INTERVAL] + tinyBreaks * m_intervals[TINY_BREAK_DURATION]; for ( int j = tick; j < ticks; j++ ) { QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); if ( j % 2 == 0 ) { idle_time_ptr->setIdleTime( 0 ); } else { idle_time_ptr->setIdleTime( 1000 ); } timer.timeout(); } for ( int i = 0; i < m_intervals[BIG_BREAK_DURATION]; i++ ) { // No activity during break -- obeying. QCOMPARE( timer.m_state, RSITimer::TimerState::Suggesting ); idle_time_ptr->setIdleTime( ( i + 1 ) * 1000 ); timer.timeout(); } QCOMPARE( timer.m_state, RSITimer::TimerState::Monitoring ); + + QCOMPARE( spyEndShortBreak.count(), tinyBreaks ); + QCOMPARE( spyEndLongBreak.count(), 1 ); } #include "rsitimer_test.moc"