Skip to content

Commit

Permalink
Add code to notify the user when their them has an update available.
Browse files Browse the repository at this point in the history
Notify a user when their current theme has an update available in
the Theme Chooser.  The Master Backend checks the downloadable themes
index once a day and downloads it if it has been updated.  The
frontends check with the Master Backend to see if their theme has
been updated.  The popup is displayed one time only.  If mythfrontend
is restarted, the popup will be displayed again.  The user can turn
off these notifications via the popup menu in the Theme Chooser.
  • Loading branch information
cpinkham committed Jan 28, 2011
1 parent 9df99f8 commit ef2cef0
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 5 deletions.
67 changes: 67 additions & 0 deletions mythtv/programs/mythbackend/housekeeper.cpp
Expand Up @@ -13,6 +13,7 @@ using namespace std;
// Qt headers
#include <QStringList>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>

// MythTV headers
Expand All @@ -26,6 +27,12 @@ using namespace std;
#include "programinfo.h"
#include "eitcache.h"
#include "scheduler.h"
#include "mythcoreutil.h"
#include "mythdownloadmanager.h"

// Use this to determine what directories to look in on the download site
extern const char *myth_source_path;
extern const char *myth_binary_version;

static bool HouseKeeper_filldb_running = false;

Expand Down Expand Up @@ -160,6 +167,7 @@ void HouseKeeper::RunHouseKeeping(void)
int period, maxhr, minhr;
QString dbTag;
bool initialRun = true;
QFileInfo zipInfo(GetConfDir() + "/tmp/remotethemes/themes.zip");

// wait a little for main server to come up and things to settle down
sleep(10);
Expand Down Expand Up @@ -265,6 +273,15 @@ void HouseKeeper::RunHouseKeeping(void)
CleanupProgramListings();
updateLastrun("DailyCleanup");
}

if ((gCoreContext->GetNumSetting("ThemeUpdateNofications", 1)) &&
((!zipInfo.exists()) ||
(zipInfo.lastModified() < mythCurrentDateTime().addDays(-2)) ||
(wantToRun("ThemeChooserInfoCacheUpdate", 1, 0, 24, true))))
{
UpdateThemeChooserInfoCache();
updateLastrun("ThemeChooserInfoCacheUpdate");
}
}

dbTag = QString("JobQueueRecover-%1").arg(gCoreContext->GetHostName());
Expand Down Expand Up @@ -619,6 +636,56 @@ void HouseKeeper::CleanupProgramListings(void)

}

void HouseKeeper::UpdateThemeChooserInfoCache(void)
{
QString MythVersion = myth_source_path;

// FIXME: For now, treat git master the same as svn trunk
if (MythVersion == "master")
MythVersion = "trunk";

if (MythVersion != "trunk")
{
MythVersion = myth_binary_version; // Example: 0.25.20101017-1
MythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
}

QString remoteThemesDir = GetConfDir();
remoteThemesDir.append("/tmp/remotethemes");

QDir dir(remoteThemesDir);
if (!dir.exists() && !dir.mkpath(remoteThemesDir))
{
VERBOSE(VB_IMPORTANT, QString("HouseKeeper: Error creating %1"
"directory for remote themes info cache.")
.arg(remoteThemesDir));
return;
}

QString remoteThemesFile = remoteThemesDir;
remoteThemesFile.append("/themes.zip");

QString url = QString("%1/%2/themes.zip")
.arg(gCoreContext->GetSetting("ThemeRepositoryURL",
"http://themes.mythtv.org/themes/repository")).arg(MythVersion);

bool result = GetMythDownloadManager()->download(url, remoteThemesFile);

if (!result)
{
VERBOSE(VB_IMPORTANT, QString("HouseKeeper: Error downloading %1"
"remote themes info package.").arg(url));
return;
}

if (!extractZIP(remoteThemesFile, remoteThemesDir))
{
VERBOSE(VB_IMPORTANT, QString("HouseKeeper: Error extracting %1"
"remote themes info package.").arg(remoteThemesFile));
QFile::remove(remoteThemesFile);
return;
}
}

void HouseKeeper::RunStartupTasks(void)
{
Expand Down
1 change: 1 addition & 0 deletions mythtv/programs/mythbackend/housekeeper.h
Expand Up @@ -33,6 +33,7 @@ class HouseKeeper
void CleanupRecordedTables(void);
void CleanupProgramListings(void);
void RunStartupTasks(void);
void UpdateThemeChooserInfoCache(void);

bool threadrunning;
bool filldbRunning;
Expand Down
8 changes: 8 additions & 0 deletions mythtv/programs/mythfrontend/main.cpp
Expand Up @@ -72,6 +72,7 @@ using namespace std;
#include "mythdirs.h"
#include "mythdb.h"
#include "backendconnectionmanager.h"
#include "themechooser.h"

static ExitPrompter *exitPopup = NULL;
static MythThemedMenu *menu;
Expand Down Expand Up @@ -1509,6 +1510,10 @@ int main(int argc, char **argv)
// Setup handler for USR2 signals to restart LIRC
signal(SIGUSR2, &signal_USR2_handler);

ThemeUpdateChecker *themeUpdateChecker = NULL;
if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
themeUpdateChecker = new ThemeUpdateChecker();

MythSystemEventHandler *sysEventHandler = new MythSystemEventHandler();
GetMythMainWindow()->RegisterSystemEventHandler(sysEventHandler);

Expand All @@ -1521,6 +1526,9 @@ int main(int argc, char **argv)

PreviewGeneratorQueue::TeardownPreviewGeneratorQueue();

if (themeUpdateChecker)
delete themeUpdateChecker;

delete sysEventHandler;

pmanager->DestroyAllPlugins();
Expand Down
157 changes: 153 additions & 4 deletions mythtv/programs/mythfrontend/themechooser.cpp
Expand Up @@ -15,6 +15,7 @@
#include "mythdownloadmanager.h"
#include "programtypes.h"
#include "mythsystemevent.h"
#include "util.h"

// LibMythUI headers
#include "mythmainwindow.h"
Expand Down Expand Up @@ -195,8 +196,17 @@ void ThemeChooser::Load(void)

int downloadFailures =
gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
if ((!QFile::exists(remoteThemesFile)) &&
(downloadFailures < 2))
if (QFile::exists(remoteThemesFile))
{
QFileInfo finfo(remoteThemesFile);
if (finfo.lastModified() < mythCurrentDateTime().addSecs(-600))
{
VERBOSE(VB_GUI, LOC + QString("%1 is over 10 minutes old, forcing "
"remote theme list download").arg(remoteThemesFile));
m_refreshDownloadableThemes = true;
}
}
else if (downloadFailures < 2) // (and themes.zip does not exist)
{
VERBOSE(VB_GUI, LOC + QString("%1 does not exist, forcing remote theme "
"list download").arg(remoteThemesFile));
Expand Down Expand Up @@ -462,6 +472,13 @@ void ThemeChooser::showPopupMenu(void)
SLOT(removeTheme()));
}
}

if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
SLOT(toggleThemeUpdateNotifications()));
else
m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
SLOT(toggleThemeUpdateNotifications()));
}

void ThemeChooser::popupClosed(QString which, int result)
Expand Down Expand Up @@ -542,6 +559,14 @@ void ThemeChooser::toggleFullscreenPreview(void)
}
}

void ThemeChooser::toggleThemeUpdateNotifications(void)
{
if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
else
gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
}

void ThemeChooser::refreshDownloadableThemes(void)
{
VERBOSE(VB_GUI, LOC + "Forcing remote theme list refresh");
Expand All @@ -566,12 +591,24 @@ void ThemeChooser::saveAndReload(MythUIButtonListItem *item)

if (!info->GetDownloadURL().isEmpty())
{
QFileInfo qfile(info->GetDownloadURL());
QString downloadURL = info->GetDownloadURL();
QFileInfo qfile(downloadURL);
QString baseName = qfile.fileName();

if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
{
QStringList tokens =
gCoreContext->GetSetting("ThemeDownloadURL")
.split(";", QString::SkipEmptyParts);
QString origURL = downloadURL;
downloadURL.replace(tokens[0], tokens[1]);
VERBOSE(VB_FILE, LOC + QString("Theme download URL overridden "
"from %1 to %2.").arg(origURL).arg(downloadURL));
}

OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
m_downloadTheme = info;
m_downloadFile = RemoteDownloadFile(info->GetDownloadURL(),
m_downloadFile = RemoteDownloadFile(downloadURL,
"Temp", baseName);
m_downloadState = dsDownloadingOnBackend;
}
Expand Down Expand Up @@ -827,5 +864,117 @@ void ThemeChooser::removeThemeDir(const QString &dirname)
dir.rmdir(dirname);
}

//////////////////////////////////////////////////////////////////////////////

ThemeUpdateChecker::ThemeUpdateChecker() :
m_updateTimer(new QTimer(this))
{
m_mythVersion = myth_source_path;

// FIXME: For now, treat git master the same as svn trunk
if (m_mythVersion == "master")
m_mythVersion = "trunk";

if (m_mythVersion != "trunk")
{
m_mythVersion = myth_binary_version; // Example: 0.25.20101017-1
m_mythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
}

m_infoPackage = QString("myth://Temp@%1/remotethemes/themes.zip")
.arg(gCoreContext->GetMasterHostName());

gCoreContext->SaveSetting("ThemeUpdateStatus", QString());

connect(m_updateTimer, SIGNAL(timeout()), SLOT(checkForUpdate()));
m_updateTimer->start(60 * 60 * 1000); // Run once an hour

// Run once 15 seconds from now
QTimer::singleShot(15 * 1000, this, SLOT(checkForUpdate()));
}

ThemeUpdateChecker::~ThemeUpdateChecker()
{
if (m_updateTimer)
{
m_updateTimer->stop();
delete m_updateTimer;
m_updateTimer = NULL;
}
}

void ThemeUpdateChecker::checkForUpdate(void)
{
if (RemoteFile::Exists(m_infoPackage))
{
QString remoteThemeDir =
QString("myth://Temp@%1/remotethemes/%2/%3")
.arg(gCoreContext->GetMasterHostName())
.arg(m_mythVersion).arg(GetMythUI()->GetThemeName());
QString infoXML = remoteThemeDir;
infoXML.append("/themeinfo.xml");

if (RemoteFile::Exists(infoXML))
{
ThemeInfo *remoteTheme = new ThemeInfo(remoteThemeDir);
if (!remoteTheme)
{
VERBOSE(VB_IMPORTANT,
QString("ThemeUpdateChecker::checkForUpdate(): "
"Unable to create ThemeInfo for %1")
.arg(infoXML));
return;
}

ThemeInfo *localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
if (!localTheme)
{
VERBOSE(VB_IMPORTANT, "ThemeUpdateChecker::checkForUpdate(): "
"Unable to create ThemeInfo for current theme");
return;
}

int rmtMaj = remoteTheme->GetMajorVersion();
int rmtMin = remoteTheme->GetMinorVersion();
int locMaj = localTheme->GetMajorVersion();
int locMin = localTheme->GetMinorVersion();

if ((rmtMaj > locMaj) ||
((rmtMaj == locMaj) &&
(rmtMin > locMin)))
{
m_lastKnownThemeVersion =
QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
.arg(rmtMaj).arg(rmtMin);

QString status = gCoreContext->GetSetting("ThemeUpdateStatus");
QString currentLocation = GetMythUI()->GetCurrentLocation(false, true);

if ((!status.startsWith(m_lastKnownThemeVersion)) &&
(currentLocation == "mainmenu"))
{
m_currentVersion = QString("%1.%2").arg(locMaj).arg(locMin);
m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);

gCoreContext->SaveSetting("ThemeUpdateStatus",
m_lastKnownThemeVersion + " notified");

QString message = tr("Version %1 of the %2 theme is now "
"available in the Theme Chooser. The "
"currently installed version is %3.")
.arg(m_newVersion)
.arg(GetMythUI()->GetThemeName())
.arg(m_currentVersion);

ShowOkPopup(message);
}
}

delete remoteTheme;
delete localTheme;
}
}
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */

31 changes: 30 additions & 1 deletion mythtv/programs/mythfrontend/themechooser.h
Expand Up @@ -6,11 +6,15 @@
#include <QList>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QObject>

// MythTV headers
#include "themeinfo.h"
#include "mythdialogbox.h"
#include "mythdirs.h"
#include "mythscreenstack.h"
#include "mythscreentype.h"
#include "themeinfo.h"

class MythDialogBox;
class MythUIButtonList;
Expand Down Expand Up @@ -43,6 +47,7 @@ class ThemeChooser : public MythScreenType
void popupClosed(QString which, int result);
void saveAndReload(void);
void toggleFullscreenPreview(void);
void toggleThemeUpdateNotifications(void);
void refreshDownloadableThemes(void);
void removeTheme(void);

Expand Down Expand Up @@ -84,4 +89,28 @@ class ThemeChooser : public MythScreenType
MythDialogBox *m_popupMenu;
};

////////////////////////////////////////////////////////////////////////////

class ThemeUpdateChecker : public QObject
{
Q_OBJECT

public:
ThemeUpdateChecker();
~ThemeUpdateChecker();

protected slots:
void checkForUpdate(void);

private:
QTimer *m_updateTimer;
QString m_mythVersion;
QString m_infoPackage;
QString m_lastKnownThemeVersion;
QString m_currentVersion;
QString m_newVersion;
};

#endif /* THEMECHOOSER */

/* vim: set expandtab tabstop=4 shiftwidth=4: */

0 comments on commit ef2cef0

Please sign in to comment.