Add settings page for Ubuntu One.

This commit is contained in:
John Maguire 2012-11-27 19:56:47 +01:00
parent 24ac9adbe5
commit 1d20ac4eae
15 changed files with 342 additions and 50 deletions

View File

@ -294,6 +294,7 @@
<file>providers/somafm.png</file> <file>providers/somafm.png</file>
<file>providers/songkick.png</file> <file>providers/songkick.png</file>
<file>providers/soundcloud.png</file> <file>providers/soundcloud.png</file>
<file>providers/ubuntuone.png</file>
<file>providers/wikipedia.png</file> <file>providers/wikipedia.png</file>
<file>sample.mood</file> <file>sample.mood</file>
<file>schema/device-schema.sql</file> <file>schema/device-schema.sql</file>

0
data/providers/podcast16.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 764 B

After

Width:  |  Height:  |  Size: 764 B

0
data/providers/podcast32.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -192,6 +192,7 @@ set(SOURCES
internet/soundcloudservice.cpp internet/soundcloudservice.cpp
internet/ubuntuoneauthenticator.cpp internet/ubuntuoneauthenticator.cpp
internet/ubuntuoneservice.cpp internet/ubuntuoneservice.cpp
internet/ubuntuonesettingspage.cpp
internet/ubuntuoneurlhandler.cpp internet/ubuntuoneurlhandler.cpp
library/groupbydialog.cpp library/groupbydialog.cpp
@ -469,6 +470,7 @@ set(HEADERS
internet/soundcloudservice.h internet/soundcloudservice.h
internet/ubuntuoneauthenticator.h internet/ubuntuoneauthenticator.h
internet/ubuntuoneservice.h internet/ubuntuoneservice.h
internet/ubuntuonesettingspage.h
internet/ubuntuoneurlhandler.h internet/ubuntuoneurlhandler.h
library/groupbydialog.h library/groupbydialog.h
@ -645,6 +647,7 @@ set(UI
internet/magnatunesettingspage.ui internet/magnatunesettingspage.ui
internet/searchboxwidget.ui internet/searchboxwidget.ui
internet/spotifysettingspage.ui internet/spotifysettingspage.ui
internet/ubuntuonesettingspage.ui
library/groupbydialog.ui library/groupbydialog.ui
library/libraryfilterwidget.ui library/libraryfilterwidget.ui

View File

@ -1,16 +1,16 @@
/* This file is part of Clementine. /* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com> Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify Clementine 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, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Clementine is distributed in the hope that it will be useful, Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>. along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/ */
@ -26,11 +26,9 @@
class GoogleDriveService; class GoogleDriveService;
class Ui_GoogleDriveSettingsPage; class Ui_GoogleDriveSettingsPage;
class QSortFilterProxyModel;
class GoogleDriveSettingsPage : public SettingsPage { class GoogleDriveSettingsPage : public SettingsPage {
Q_OBJECT Q_OBJECT
public: public:
GoogleDriveSettingsPage(SettingsDialog* parent = 0); GoogleDriveSettingsPage(SettingsDialog* parent = 0);
~GoogleDriveSettingsPage(); ~GoogleDriveSettingsPage();

View File

@ -1,18 +1,24 @@
#include "ubuntuoneauthenticator.h" #include "ubuntuoneauthenticator.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDateTime>
#include <QHostInfo> #include <QHostInfo>
#include <QStringList>
#include <qjson/parser.h> #include <qjson/parser.h>
#include "core/closure.h" #include "core/closure.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/network.h" #include "core/network.h"
#include "core/timeconstants.h"
namespace { namespace {
static const char* kUbuntuOneEndpoint = static const char* kUbuntuOneEndpoint =
"https://login.ubuntu.com/api/1.0/authentications"; "https://login.ubuntu.com/api/1.0/authentications";
static const char* kTokenNameTemplate = "Ubuntu One @ %1 [%2]"; static const char* kTokenNameTemplate = "Ubuntu One @ %1 [%2]";
static const char* kOAuthSSOFinishedEndpoint =
"https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/";
static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", ";
} }
UbuntuOneAuthenticator::UbuntuOneAuthenticator(QObject* parent) UbuntuOneAuthenticator::UbuntuOneAuthenticator(QObject* parent)
@ -66,5 +72,67 @@ void UbuntuOneAuthenticator::AuthorisationFinished(QNetworkReply* reply) {
token_ = auth_info["token"].toString(); token_ = auth_info["token"].toString();
token_secret_ = auth_info["token_secret"].toString(); token_secret_ = auth_info["token_secret"].toString();
CopySSOTokens();
}
QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader(
const QString& consumer_key,
const QString& consumer_secret,
const QString& token,
const QString& token_secret) {
typedef QPair<QString, QString> Param;
QString timestamp = QString::number(
QDateTime::currentMSecsSinceEpoch() / kMsecPerSec);
QList<Param> parameters;
parameters << Param("oauth_nonce", QString::number(qrand()))
<< Param("oauth_timestamp", timestamp)
<< Param("oauth_version", "1.0")
<< Param("oauth_consumer_key", consumer_key)
<< Param("oauth_token", token)
<< Param("oauth_signature_method", "PLAINTEXT");
qSort(parameters.begin(), parameters.end());
QStringList encoded_params;
for (const Param& p : parameters) {
encoded_params << QString("%1=%2").arg(p.first, p.second);
}
QString signing_key =
consumer_secret + "&" + token_secret;
QByteArray signature = QUrl::toPercentEncoding(signing_key);
// Construct authorisation header
parameters << Param("oauth_signature", signature);
QStringList header_params;
for (const Param& p : parameters) {
header_params << QString("%1=\"%2\"").arg(p.first, p.second);
}
QString authorisation_header = header_params.join(", ");
authorisation_header.prepend(kOAuthHeaderPrefix);
return authorisation_header.toAscii();
}
QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader() {
return GenerateAuthorisationHeader(
consumer_key_,
consumer_secret_,
token_,
token_secret_);
}
void UbuntuOneAuthenticator::CopySSOTokens() {
QUrl url(kOAuthSSOFinishedEndpoint);
QNetworkRequest request(url);
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
request.setRawHeader("Accept", "application/json");
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()),
this, SLOT(CopySSOTokensFinished(QNetworkReply*)), reply);
}
void UbuntuOneAuthenticator::CopySSOTokensFinished(QNetworkReply* reply) {
reply->deleteLater();
qLog(Debug) << reply->readAll();
emit Finished(); emit Finished();
} }

View File

@ -17,11 +17,22 @@ class UbuntuOneAuthenticator : public QObject {
QString token() const { return token_; } QString token() const { return token_; }
QString token_secret() const { return token_secret_; } QString token_secret() const { return token_secret_; }
static QByteArray GenerateAuthorisationHeader(
const QString& consumer_key,
const QString& consumer_secret,
const QString& token,
const QString& token_secret);
signals: signals:
void Finished(); void Finished();
private slots: private slots:
void AuthorisationFinished(QNetworkReply* reply); void AuthorisationFinished(QNetworkReply* reply);
void CopySSOTokensFinished(QNetworkReply* reply);
private:
void CopySSOTokens();
QByteArray GenerateAuthorisationHeader();
private: private:
NetworkAccessManager* network_; NetworkAccessManager* network_;

View File

@ -1,6 +1,7 @@
#include "ubuntuoneservice.h" #include "ubuntuoneservice.h"
#include <QDateTime> #include <QDateTime>
#include <QSettings>
#include <qjson/parser.h> #include <qjson/parser.h>
@ -21,10 +22,7 @@ const char* UbuntuOneService::kSettingsGroup = "Ubuntu One";
namespace { namespace {
static const char* kFileStorageEndpoint = static const char* kFileStorageEndpoint =
"https://one.ubuntu.com/api/file_storage/v1/~/Ubuntu One/"; "https://one.ubuntu.com/api/file_storage/v1/~/Ubuntu One/";
static const char* kOAuthSSOFinishedEndpoint =
"https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/";
static const char* kContentRoot = "https://files.one.ubuntu.com"; static const char* kContentRoot = "https://files.one.ubuntu.com";
static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", ";
} }
UbuntuOneService::UbuntuOneService(Application* app, InternetModel* parent) UbuntuOneService::UbuntuOneService(Application* app, InternetModel* parent)
@ -52,6 +50,18 @@ void UbuntuOneService::LazyPopulate(QStandardItem* item) {
} }
void UbuntuOneService::Connect() { void UbuntuOneService::Connect() {
QSettings s;
s.beginGroup(kSettingsGroup);
if (s.contains("consumer_key")) {
consumer_key_ = s.value("consumer_key").toString();
consumer_secret_ = s.value("consumer_secret").toString();
token_ = s.value("token").toString();
token_secret_ = s.value("token_secret").toString();
RequestFileList();
return;
}
UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator; UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator;
authenticator->StartAuthorisation( authenticator->StartAuthorisation(
"Username", "Username",
@ -62,36 +72,11 @@ void UbuntuOneService::Connect() {
} }
QByteArray UbuntuOneService::GenerateAuthorisationHeader() { QByteArray UbuntuOneService::GenerateAuthorisationHeader() {
typedef QPair<QString, QString> Param; return UbuntuOneAuthenticator::GenerateAuthorisationHeader(
QString timestamp = QString::number( consumer_key_,
QDateTime::currentMSecsSinceEpoch() / kMsecPerSec); consumer_secret_,
QList<Param> parameters; token_,
parameters << Param("oauth_nonce", QString::number(qrand())) token_secret_);
<< Param("oauth_timestamp", timestamp)
<< Param("oauth_version", "1.0")
<< Param("oauth_consumer_key", consumer_key_)
<< Param("oauth_token", token_)
<< Param("oauth_signature_method", "PLAINTEXT");
qSort(parameters.begin(), parameters.end());
QStringList encoded_params;
for (const Param& p : parameters) {
encoded_params << QString("%1=%2").arg(p.first, p.second);
}
QString signing_key =
consumer_secret_ + "&" + token_secret_;
QByteArray signature = QUrl::toPercentEncoding(signing_key);
// Construct authorisation header
parameters << Param("oauth_signature", signature);
QStringList header_params;
for (const Param& p : parameters) {
header_params << QString("%1=\"%2\"").arg(p.first, p.second);
}
QString authorisation_header = header_params.join(", ");
authorisation_header.prepend(kOAuthHeaderPrefix);
return authorisation_header.toAscii();
} }
void UbuntuOneService::AuthenticationFinished( void UbuntuOneService::AuthenticationFinished(
@ -103,19 +88,17 @@ void UbuntuOneService::AuthenticationFinished(
token_ = authenticator->token(); token_ = authenticator->token();
token_secret_ = authenticator->token_secret(); token_secret_ = authenticator->token_secret();
QUrl sso_url(kOAuthSSOFinishedEndpoint); QSettings s;
QNetworkRequest request(sso_url); s.beginGroup(kSettingsGroup);
request.setRawHeader("Authorization", GenerateAuthorisationHeader()); s.setValue("consumer_key", consumer_key_);
request.setRawHeader("Accept", "application/json"); s.setValue("consumer_secret", consumer_secret_);
s.setValue("token", token_);
s.setValue("token_secret", token_secret_);
qLog(Debug) << "Sending SSO copy request"; RequestFileList();
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()),
this, SLOT(SSORequestFinished(QNetworkReply*)), reply);
} }
void UbuntuOneService::SSORequestFinished(QNetworkReply* reply) { void UbuntuOneService::RequestFileList() {
qLog(Debug) << Q_FUNC_INFO;
QUrl files_url(kFileStorageEndpoint); QUrl files_url(kFileStorageEndpoint);
files_url.addQueryItem("include_children", "true"); files_url.addQueryItem("include_children", "true");
QNetworkRequest request(files_url); QNetworkRequest request(files_url);

View File

@ -23,11 +23,11 @@ class UbuntuOneService : public InternetService {
private slots: private slots:
void AuthenticationFinished(UbuntuOneAuthenticator* authenticator); void AuthenticationFinished(UbuntuOneAuthenticator* authenticator);
void SSORequestFinished(QNetworkReply* reply);
void FileListRequestFinished(QNetworkReply* reply); void FileListRequestFinished(QNetworkReply* reply);
private: private:
void Connect(); void Connect();
void RequestFileList();
private: private:
QByteArray GenerateAuthorisationHeader(); QByteArray GenerateAuthorisationHeader();

View File

@ -0,0 +1,74 @@
#include "ubuntuonesettingspage.h"
#include "ui_ubuntuonesettingspage.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "internet/internetmodel.h"
#include "internet/ubuntuoneauthenticator.h"
#include "internet/ubuntuoneservice.h"
UbuntuOneSettingsPage::UbuntuOneSettingsPage(SettingsDialog* parent)
: SettingsPage(parent),
ui_(new Ui::UbuntuOneSettingsPage),
service_(dialog()->app()->internet_model()->Service<UbuntuOneService>()),
authenticated_(false) {
ui_->setupUi(this);
ui_->login_state->AddCredentialGroup(ui_->login_container);
connect(ui_->login_button, SIGNAL(clicked()), SLOT(LoginClicked()));
dialog()->installEventFilter(this);
}
void UbuntuOneSettingsPage::Load() {
QSettings s;
s.beginGroup(UbuntuOneService::kSettingsGroup);
const QString user_email = s.value("user_email").toString();
if (!user_email.isEmpty()) {
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, user_email);
ui_->username->setText(user_email);
}
}
void UbuntuOneSettingsPage::Save() {
QSettings s;
s.beginGroup(UbuntuOneService::kSettingsGroup);
if (authenticated_) {
s.setValue("user_email", ui_->username->text());
}
}
void UbuntuOneSettingsPage::LoginClicked() {
ui_->login_button->setEnabled(false);
QString username = ui_->username->text();
QString password = ui_->password->text();
UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator;
authenticator->StartAuthorisation(username, password);
NewClosure(authenticator, SIGNAL(Finished()),
this, SLOT(Connected(UbuntuOneAuthenticator*)), authenticator);
NewClosure(authenticator, SIGNAL(Finished()),
service_, SLOT(AuthenticationFinished(UbuntuOneAuthenticator*)),
authenticator);
}
void UbuntuOneSettingsPage::LogoutClicked() {
}
void UbuntuOneSettingsPage::Connected(UbuntuOneAuthenticator* authenticator) {
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, ui_->username->text());
authenticated_ = true;
}
bool UbuntuOneSettingsPage::eventFilter(QObject* object, QEvent* event) {
if (object == dialog() && event->type() == QEvent::Enter) {
ui_->login_button->setEnabled(true);
return false;
}
return SettingsPage::eventFilter(object, event);
}

View File

@ -0,0 +1,33 @@
#ifndef UBUNTUONESETTINGSPAGE_H
#define UBUNTUONESETTINGSPAGE_H
#include "ui/settingspage.h"
class UbuntuOneAuthenticator;
class UbuntuOneService;
class Ui_UbuntuOneSettingsPage;
class UbuntuOneSettingsPage : public SettingsPage {
Q_OBJECT
public:
UbuntuOneSettingsPage(SettingsDialog* parent = 0);
void Load();
void Save();
// QObject
bool eventFilter(QObject* object, QEvent* event);
private slots:
void LoginClicked();
void LogoutClicked();
void Connected(UbuntuOneAuthenticator* authenticator);
private:
Ui_UbuntuOneSettingsPage* ui_;
UbuntuOneService* service_;
bool authenticated_;
};
#endif // UBUNTUONESETTINGSPAGE_H

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UbuntuOneSettingsPage</class>
<widget class="QWidget" name="UbuntuOneSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string>Ubuntu One</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/providers/ubuntuone.png</normaloff>:/providers/ubuntuone.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Clementine can play music that you have uploaded to Ubuntu One</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>
<item>
<widget class="QWidget" name="login_container" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>28</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="login_button">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="username">
<property name="placeholderText">
<string>Email address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="password">
<property name="placeholderText">
<string>Password</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>357</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LoginStateWidget</class>
<extends>QWidget</extends>
<header>widgets/loginstatewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -37,6 +37,7 @@
#include "internet/digitallyimportedsettingspage.h" #include "internet/digitallyimportedsettingspage.h"
#include "internet/groovesharksettingspage.h" #include "internet/groovesharksettingspage.h"
#include "internet/magnatunesettingspage.h" #include "internet/magnatunesettingspage.h"
#include "internet/ubuntuonesettingspage.h"
#include "library/librarysettingspage.h" #include "library/librarysettingspage.h"
#include "playlist/playlistview.h" #include "playlist/playlistview.h"
#include "podcasts/podcastsettingspage.h" #include "podcasts/podcastsettingspage.h"
@ -147,6 +148,8 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams, QWi
AddPage(Page_GoogleDrive, new GoogleDriveSettingsPage(this), providers); AddPage(Page_GoogleDrive, new GoogleDriveSettingsPage(this), providers);
#endif #endif
AddPage(Page_UbuntuOne, new UbuntuOneSettingsPage(this), providers);
#ifdef HAVE_SPOTIFY #ifdef HAVE_SPOTIFY
AddPage(Page_Spotify, new SpotifySettingsPage(this), providers); AddPage(Page_Spotify, new SpotifySettingsPage(this), providers);
#endif #endif

View File

@ -77,6 +77,7 @@ public:
Page_Wiimotedev, Page_Wiimotedev,
Page_Podcasts, Page_Podcasts,
Page_GoogleDrive, Page_GoogleDrive,
Page_UbuntuOne,
}; };
enum Role { enum Role {