// -*- c++ -*- /*! * * Copyright (C) 2015 Jolla Ltd. * * Contact: Valerio Valerio * Author: Andres Gomez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mprisplayer_p.h" #include "mprisplayer.h" #include #include #include #include #include #include #include #include #include /* * Implementation of adaptor class MprisPlayerAdaptor */ static const QString mprisPlayerInterface = QStringLiteral("org.mpris.MediaPlayer2.Player"); MprisPlayerAdaptor::MprisPlayerAdaptor(MprisPlayer *parent) : QDBusAbstractAdaptor(parent) { // constructor setAutoRelaySignals(false); connect(parent, SIGNAL(canControlChanged()), this, SLOT(onCanControlChanged())); connect(parent, SIGNAL(canGoNextChanged()), this, SLOT(onCanGoNextChanged())); connect(parent, SIGNAL(canGoPreviousChanged()), this, SLOT(onCanGoPreviousChanged())); connect(parent, SIGNAL(canPauseChanged()), this, SLOT(onCanPauseChanged())); connect(parent, SIGNAL(canPlayChanged()), this, SLOT(onCanPlayChanged())); connect(parent, SIGNAL(canSeekChanged()), this, SLOT(onCanSeekChanged())); connect(parent, SIGNAL(loopStatusChanged()), this, SLOT(onLoopStatusChanged())); connect(parent, SIGNAL(maximumRateChanged()), this, SLOT(onMaximumRateChanged())); connect(parent, SIGNAL(metadataChanged()), this, SLOT(onMetadataChanged())); connect(parent, SIGNAL(minimumRateChanged()), this, SLOT(onMinimumRateChanged())); connect(parent, SIGNAL(playbackStatusChanged()), this, SLOT(onPlaybackStatusChanged())); // PositionChanged signal is not forwarded through DBus ... connect(parent, SIGNAL(rateChanged()), this, SLOT(onRateChanged())); connect(parent, SIGNAL(shuffleChanged()), this, SLOT(onShuffleChanged())); connect(parent, SIGNAL(volumeChanged()), this, SLOT(onVolumeChanged())); connect(parent, SIGNAL(seeked(qlonglong)), this, SIGNAL(Seeked(qlonglong))); } MprisPlayerAdaptor::~MprisPlayerAdaptor() { // destructor } bool MprisPlayerAdaptor::canControl() const { return static_cast(parent())->canControl(); } bool MprisPlayerAdaptor::canGoNext() const { MprisPlayer * const player = static_cast(parent()); return player->canControl() && player->canGoNext(); } bool MprisPlayerAdaptor::canGoPrevious() const { MprisPlayer * const player = static_cast(parent()); return player->canControl() && player->canGoPrevious(); } bool MprisPlayerAdaptor::canPause() const { MprisPlayer * const player = static_cast(parent()); return player->canControl() && player->canPause(); } bool MprisPlayerAdaptor::canPlay() const { MprisPlayer * const player = static_cast(parent()); return player->canControl() && player->canPlay(); } bool MprisPlayerAdaptor::canSeek() const { MprisPlayer * const player = static_cast(parent()); return player->canControl() && player->canSeek(); } QString MprisPlayerAdaptor::loopStatus() const { return Mpris::enumerationToString(static_cast(parent())->loopStatus()); } void MprisPlayerAdaptor::setLoopStatus(const QString &value) { MprisPlayer * const player = static_cast(parent()); if (player->canControl()) { Q_EMIT player->loopStatusRequested(Mpris::enumerationFromString(value)); return; } // We cannot send an error reply in a property setter so we just // complain here qDebug() << Q_FUNC_INFO << "Requested to modify the loop status but it is not supported."; } double MprisPlayerAdaptor::maximumRate() const { return static_cast(parent())->maximumRate(); } QVariantMap MprisPlayerAdaptor::metadata() const { return static_cast(parent())->metadata(); } double MprisPlayerAdaptor::minimumRate() const { return static_cast(parent())->minimumRate(); } QString MprisPlayerAdaptor::playbackStatus() const { return Mpris::enumerationToString(static_cast(parent())->playbackStatus()); } qlonglong MprisPlayerAdaptor::position() const { return static_cast(parent())->position(); } double MprisPlayerAdaptor::rate() const { return static_cast(parent())->rate(); } void MprisPlayerAdaptor::setRate(double value) { MprisPlayer * const player = static_cast(parent()); QString errorMessage; if (!player->canControl()) { errorMessage = QStringLiteral("Requested to modify the rate but it is not supported."); } else if (player->minimumRate() > value) { errorMessage = QStringLiteral("Requested to modify the rate under its minimum allowed value."); } else if (player->maximumRate() < value) { errorMessage = QStringLiteral("Requested to modify the rate over its maximum allowed value."); } if (errorMessage.isEmpty()) { if (value == 0) { Q_EMIT player->pauseRequested(); } else { Q_EMIT player->rateRequested(value); } return; } // We cannot send an error reply in a property setter so we just // complain here qDebug() << Q_FUNC_INFO << errorMessage; } bool MprisPlayerAdaptor::shuffle() const { return static_cast(parent())->shuffle(); } void MprisPlayerAdaptor::setShuffle(bool value) { MprisPlayer * const player = static_cast(parent()); if (player->canControl()) { Q_EMIT player->shuffleRequested(value); return; } // We cannot send an error reply in a property setter so we just // complain here qDebug() << Q_FUNC_INFO << "Requested to modify the shuffle but it is not supported."; } double MprisPlayerAdaptor::volume() const { return static_cast(parent())->volume(); } void MprisPlayerAdaptor::setVolume(double value) { MprisPlayer * const player = static_cast(parent()); if (player->canControl()) { Q_EMIT player->volumeRequested(value < 0 ? 0 : value); return; } // We cannot send an error reply in a property setter so we just // complain here qDebug() << Q_FUNC_INFO << "Requested to modify the volume but it is not supported."; } void MprisPlayerAdaptor::Next() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to next track but it is not supported.")); return; } if (!player->canGoNext()) { return; } Q_EMIT player->nextRequested(); } void MprisPlayerAdaptor::OpenUri(const QString &Uri) { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but it is not supported.")); return; } QUrl url(Uri, QUrl::StrictMode); if (!url.isValid()) { player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to open an url but the url is invalid.")); } if (!player->supportedUriSchemes().contains(url.scheme())) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but the scheme is not supported.")); } QMimeDatabase db; QMimeType mime; if (url.isLocalFile()) { mime = db.mimeTypeForFile(url.toLocalFile()); } else { mime = db.mimeTypeForFile(url.fileName(), QMimeDatabase::MatchExtension); } QStringList mimeNames = mime.aliases(); mimeNames.prepend(mime.name()); for (int i = 0; i < mimeNames.size(); i++) { if (player->supportedMimeTypes().contains(mimeNames[i])) { Q_EMIT player->openUriRequested(url); return; } } player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but the mime type is not supported.")); } void MprisPlayerAdaptor::Pause() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to pause but it is not supported.")); return; } if (!player->canPause()) { return; } switch (player->playbackStatus()) { case Mpris::Playing: case Mpris::Stopped: Q_EMIT player->pauseRequested(); break; case Mpris::Paused: default: // Nothing to do break; } } void MprisPlayerAdaptor::Play() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play but it is not supported.")); return; } if (!player->canPlay()) { return; } switch (player->playbackStatus()) { case Mpris::Stopped: case Mpris::Paused: Q_EMIT player->playRequested(); break; case Mpris::Playing: default: // Nothing to do break; } } void MprisPlayerAdaptor::PlayPause() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play or pause but it is not supported.")); return; } switch (player->playbackStatus()) { case Mpris::Playing: if (!player->canPause()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to pause but it is not supported.")); return; } Q_EMIT player->pauseRequested(); break; case Mpris::Stopped: case Mpris::Paused: if (!player->canPlay()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play but it is not supported.")); return; } Q_EMIT player->playRequested(); break; default: // Nothing to do break; } } void MprisPlayerAdaptor::Previous() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to previous track but it is not supported.")); return; } if (!player->canGoPrevious()) { return; } Q_EMIT player->previousRequested(); } void MprisPlayerAdaptor::Seek(qlonglong Offset) { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to seek but it is not supported.")); return; } if (!player->canSeek() || Offset == 0) { return; } if (Offset < 0) { Q_EMIT player->seekRequested(Offset); return; } QVariantMap metadata = player->metadata(); QVariant length = metadata[Mpris::metadataToString(Mpris::Length)]; if (length.isValid() && (player->position() + Offset) > length.toLongLong()) { Q_EMIT player->nextRequested(); return; } Q_EMIT player->seekRequested(Offset); } void MprisPlayerAdaptor::SetPosition(const QDBusObjectPath &TrackId, qlonglong Position) { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to position but it is not supported.")); return; } if (!player->canSeek()) { return; } QVariantMap metadata = player->metadata(); QVariant trackId = metadata[Mpris::metadataToString(Mpris::TrackId)]; QVariant length = metadata[Mpris::metadataToString(Mpris::Length)]; if (!trackId.isValid() || !length.isValid()) { return; } if (trackId.value() != TrackId) { player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to move to position but the TrackId is not the current one.")); return; } if (Position > length.toLongLong()) { player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to move to position but the position is off range.")); return; } Q_EMIT player->setPositionRequested(TrackId, Position); } void MprisPlayerAdaptor::Stop() { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to stop but it is not supported.")); return; } switch (player->playbackStatus()) { case Mpris::Playing: case Mpris::Paused: Q_EMIT player->stopRequested(); break; case Mpris::Stopped: default: // Nothing to do break; } } // Private void MprisPlayerAdaptor::onCanControlChanged() const { MprisPlayer * const player = static_cast(parent()); // canControlChanged signal is not forwarded through DBus, but // we can notify about the rest of "Can*" properties QVariantMap changedProperties; if (player->canGoNext()) { changedProperties[QStringLiteral("CanGoNext")] = QVariant(player->canControl()); } if (player->canGoPrevious()) { changedProperties[QStringLiteral("CanGoPrevious")] = QVariant(player->canControl()); } if (player->canPause()) { changedProperties[QStringLiteral("CanPause")] = QVariant(player->canControl()); } if (player->canPlay()) { changedProperties[QStringLiteral("CanPlay")] = QVariant(player->canControl()); } if (player->canSeek()) { changedProperties[QStringLiteral("CanSeek")] = QVariant(player->canControl()); } if (changedProperties.isEmpty()) { return; } player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onCanGoNextChanged() const { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { return; } QVariantMap changedProperties; changedProperties[QStringLiteral("CanGoNext")] = QVariant(player->canGoNext()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onCanGoPreviousChanged() const { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { return; } QVariantMap changedProperties; changedProperties[QStringLiteral("CanGoPrevious")] = QVariant(player->canGoPrevious()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onCanPauseChanged() const { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { return; } QVariantMap changedProperties; changedProperties[QStringLiteral("CanPause")] = QVariant(player->canPause()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onCanPlayChanged() const { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { return; } QVariantMap changedProperties; changedProperties[QStringLiteral("CanPlay")] = QVariant(player->canPlay()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onCanSeekChanged() const { MprisPlayer * const player = static_cast(parent()); if (!player->canControl()) { return; } QVariantMap changedProperties; changedProperties[QStringLiteral("CanSeek")] = QVariant(player->canSeek()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onLoopStatusChanged() const { MprisPlayer * const player = static_cast(parent()); QVariantMap changedProperties; changedProperties[QStringLiteral("LoopStatus")] = QVariant(Mpris::enumerationToString(player->loopStatus())); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onMaximumRateChanged() const { MprisPlayer * const player = static_cast(parent()); if (player->maximumRate() < 1) { qmlInfo(this) << "Maximum rate should be equal or above 1"; return; } QVariantMap changedProperties; changedProperties[QStringLiteral("MaximumRate")] = QVariant(player->maximumRate()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onMetadataChanged() const { MprisPlayer * const player = static_cast(parent()); QVariantMap changedProperties; changedProperties[QStringLiteral("Metadata")] = QVariant(player->metadata()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onMinimumRateChanged() const { MprisPlayer * const player = static_cast(parent()); if (player->minimumRate() > 1) { qmlInfo(this) << "Minimum rate should be equal or less than 1"; return; } QVariantMap changedProperties; changedProperties[QStringLiteral("MinimumRate")] = QVariant(player->minimumRate() < 0 ? 0 : player->minimumRate()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onPlaybackStatusChanged() const { MprisPlayer * const player = static_cast(parent()); QVariantMap changedProperties; changedProperties[QStringLiteral("PlaybackStatus")] = QVariant(Mpris::enumerationToString(player->playbackStatus())); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onRateChanged() const { MprisPlayer * const player = static_cast(parent()); if (player->rate() <= 0 || player->rate() < player->minimumRate() || player->rate() > player->maximumRate()) { qmlInfo(this) << "Rate should never be negative or out of the minimum and maximum limits"; return; } QVariantMap changedProperties; changedProperties[QStringLiteral("Rate")] = QVariant(player->rate()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onShuffleChanged() const { MprisPlayer * const player = static_cast(parent()); QVariantMap changedProperties; changedProperties[QStringLiteral("Shuffle")] = QVariant(player->shuffle()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); } void MprisPlayerAdaptor::onVolumeChanged() const { MprisPlayer * const player = static_cast(parent()); QVariantMap changedProperties; changedProperties[QStringLiteral("Volume")] = QVariant(player->volume() < 0 ? 0 : player->volume()); player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList()); }