File indexing completed on 2024-05-19 05:04:05

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "playsoundwidget.h"
0008 #include "misc/messageattachmentdownloadandsavejob.h"
0009 #include "rocketchataccount.h"
0010 #include "ruqolaglobalconfig.h"
0011 
0012 #include <KLocalizedString>
0013 #include <KMessageWidget>
0014 
0015 #include <QAudioDevice>
0016 #include <QAudioOutput>
0017 #include <QComboBox>
0018 #include <QFontMetrics>
0019 #include <QHBoxLayout>
0020 #include <QLabel>
0021 #include <QMediaDevices>
0022 #include <QPushButton>
0023 #include <QSlider>
0024 #include <QStyle>
0025 #include <QTime>
0026 #include <QToolButton>
0027 
0028 PlaySoundWidget::PlaySoundWidget(RocketChatAccount *account, QWidget *parent)
0029     : QWidget(parent)
0030     , mMediaPlayer(new QMediaPlayer(this))
0031     , mPlayButton(new QPushButton(this))
0032     , mSoundButton(new QToolButton(this))
0033     , mSoundSlider(new QSlider(Qt::Horizontal, this))
0034     , mPositionSlider(new QSlider(Qt::Horizontal, this))
0035     , mLabelDuration(new QLabel(this))
0036     , mMessageWidget(new KMessageWidget(this))
0037     , mLabelPercentSound(new QLabel(this))
0038     , mAudioOutput(new QAudioOutput(this))
0039     , mDeviceComboBox(new QComboBox(this))
0040     , mRocketChatAccount(account)
0041 {
0042     mMediaPlayer->setAudioOutput(mAudioOutput);
0043     mDeviceComboBox->setObjectName(QStringLiteral("mDeviceComboBox"));
0044     initializeAudioOutput();
0045 
0046     auto mainLayout = new QVBoxLayout(this);
0047     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0048     mainLayout->setContentsMargins({});
0049     mainLayout->addWidget(mDeviceComboBox);
0050 
0051     auto playerLayout = new QHBoxLayout;
0052     playerLayout->setObjectName(QStringLiteral("playerLayout"));
0053     playerLayout->setContentsMargins({});
0054     mainLayout->addLayout(playerLayout);
0055     mMessageWidget->setObjectName(QStringLiteral("mMessageWidget"));
0056     mainLayout->addWidget(mMessageWidget);
0057     mMessageWidget->setVisible(false);
0058     mMessageWidget->setCloseButtonVisible(false);
0059     mMessageWidget->setMessageType(KMessageWidget::Information);
0060     mMessageWidget->setWordWrap(true);
0061     mLabelDuration->setObjectName(QStringLiteral("mLabelDuration"));
0062     mLabelPercentSound->setObjectName(QStringLiteral("mLabelPercentSound"));
0063 
0064     mLabelDuration->setTextFormat(Qt::PlainText);
0065     mLabelPercentSound->setTextFormat(Qt::PlainText);
0066 
0067     mMediaPlayer->setObjectName(QStringLiteral("mMediaPlayer"));
0068 
0069     mPositionSlider->setObjectName(QStringLiteral("mPositionSlider"));
0070     mPositionSlider->setRange(0, 100);
0071     mPositionSlider->setValue(100);
0072     connect(mPositionSlider, &QAbstractSlider::sliderMoved, this, &PlaySoundWidget::setPosition);
0073 
0074     mSoundSlider->setObjectName(QStringLiteral("mSoundSlider"));
0075     mSoundSlider->setRange(0, 100);
0076     mSoundSlider->setValue(RuqolaGlobalConfig::self()->soundVolume());
0077     mSoundSlider->setTickPosition(QSlider::TicksAbove);
0078 
0079     connect(mSoundSlider, &QAbstractSlider::sliderMoved, this, &PlaySoundWidget::slotVolumeChanged);
0080 
0081     connect(mMediaPlayer, &QMediaPlayer::positionChanged, this, &PlaySoundWidget::slotPositionChanged);
0082     connect(mMediaPlayer, &QMediaPlayer::durationChanged, this, &PlaySoundWidget::slotDurationChanged);
0083 
0084     // Allow to change volume
0085 
0086     connect(mMediaPlayer, &QMediaPlayer::playbackStateChanged, this, &PlaySoundWidget::mediaStateChanged);
0087     mPlayButton->setObjectName(QStringLiteral("mPlayButton"));
0088     mPlayButton->setEnabled(false);
0089     mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0090     playerLayout->addWidget(mPlayButton);
0091     connect(mPlayButton, &QAbstractButton::clicked, this, &PlaySoundWidget::play);
0092 
0093     mSoundButton->setCheckable(true);
0094     mSoundButton->setObjectName(QStringLiteral("mSoundButton"));
0095     mSoundButton->setIcon(QIcon::fromTheme(QStringLiteral("player-volume")));
0096     connect(mSoundButton, &QToolButton::clicked, mAudioOutput, &QAudioOutput::setMuted);
0097     connect(mAudioOutput, &QAudioOutput::mutedChanged, this, &PlaySoundWidget::muteChanged);
0098     playerLayout->addWidget(mPositionSlider);
0099 
0100     playerLayout->addWidget(mSoundButton);
0101 
0102     playerLayout->addWidget(mLabelDuration);
0103 
0104     connect(mMediaPlayer, &QMediaPlayer::errorChanged, this, &PlaySoundWidget::handleError);
0105 
0106     playerLayout->addWidget(mSoundSlider);
0107     playerLayout->addWidget(mLabelPercentSound);
0108 
0109     QFontMetrics f(font());
0110     mLabelPercentSound->setFixedWidth(f.horizontalAdvance(QStringLiteral("MMM")));
0111     slotVolumeChanged(mSoundSlider->value());
0112 }
0113 
0114 PlaySoundWidget::~PlaySoundWidget()
0115 {
0116     RuqolaGlobalConfig::self()->setSoundVolume(mSoundSlider->value());
0117     RuqolaGlobalConfig::self()->save();
0118 }
0119 
0120 void PlaySoundWidget::initializeAudioOutput()
0121 {
0122     mDeviceComboBox->addItem(i18n("Default"), QVariant::fromValue(QAudioDevice()));
0123     for (const auto &deviceInfo : QMediaDevices::audioOutputs()) {
0124         mDeviceComboBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
0125     }
0126     connect(mDeviceComboBox, &QComboBox::activated, this, &PlaySoundWidget::audioOutputChanged);
0127 }
0128 
0129 void PlaySoundWidget::audioOutputChanged(int index)
0130 {
0131     const auto device = mDeviceComboBox->itemData(index).value<QAudioDevice>();
0132     mMediaPlayer->audioOutput()->setDevice(device);
0133 }
0134 
0135 void PlaySoundWidget::slotPositionChanged(qint64 progress)
0136 {
0137     if (!mPositionSlider->isSliderDown())
0138         mPositionSlider->setValue(progress / 1000);
0139 
0140     updateDurationInfo(progress / 1000);
0141 }
0142 
0143 void PlaySoundWidget::updateDurationInfo(qint64 currentInfo)
0144 {
0145     QString tStr;
0146     if (currentInfo || mDuration) {
0147         const QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000);
0148         const QTime totalTime((mDuration / 3600) % 60, (mDuration / 60) % 60, mDuration % 60, (mDuration * 1000) % 1000);
0149         QString format = QStringLiteral("mm:ss");
0150         if (mDuration > 3600)
0151             format = QStringLiteral("hh:mm:ss");
0152         tStr = currentTime.toString(format) + QStringLiteral(" / ") + totalTime.toString(format);
0153     }
0154     mLabelDuration->setText(tStr);
0155 }
0156 
0157 void PlaySoundWidget::slotVolumeChanged(int position)
0158 {
0159     mAudioOutput->setVolume(position / 100.0);
0160     mLabelPercentSound->setText(QStringLiteral("%1%").arg(position));
0161 }
0162 
0163 void PlaySoundWidget::slotDurationChanged(qint64 duration)
0164 {
0165     mDuration = duration / 1000;
0166     mPositionSlider->setMaximum(mDuration);
0167 }
0168 
0169 void PlaySoundWidget::setPosition(int position)
0170 {
0171     mMediaPlayer->setPosition(position * 1000);
0172 }
0173 
0174 void PlaySoundWidget::muteChanged(bool state)
0175 {
0176     mSoundButton->setIcon(state ? QIcon::fromTheme(QStringLiteral("player-volume-muted")) : QIcon::fromTheme(QStringLiteral("player-volume")));
0177 }
0178 
0179 QUrl PlaySoundWidget::audioUrl() const
0180 {
0181     return mMediaPlayer->source();
0182 }
0183 
0184 void PlaySoundWidget::slotAttachmentFileDownloadDone(const QString &url)
0185 {
0186     const QUrl localUrl = QUrl::fromLocalFile(url);
0187     Q_EMIT updateTitle(localUrl);
0188     // setWindowFilePath(localUrl);
0189     mMediaPlayer->setSource(localUrl);
0190     mPlayButton->setEnabled(true);
0191 }
0192 
0193 void PlaySoundWidget::setAudioPath(const QString &url)
0194 {
0195     if (mRocketChatAccount) {
0196         MessageAttachmentDownloadAndSaveJob::MessageAttachmentDownloadJobInfo info;
0197         info.attachmentType = MessageAttachmentDownloadAndSaveJob::AttachmentType::Sound;
0198         info.actionType = MessageAttachmentDownloadAndSaveJob::ActionType::DownloadOnly;
0199         info.needToDownloadAttachment = !mRocketChatAccount->attachmentIsInLocalCache(url);
0200         info.parentWidget = this;
0201         info.attachmentPath = url;
0202         auto job = new MessageAttachmentDownloadAndSaveJob(this);
0203         connect(job, &MessageAttachmentDownloadAndSaveJob::attachmentFileDownloadDone, this, &PlaySoundWidget::slotAttachmentFileDownloadDone);
0204         job->setRocketChatAccount(mRocketChatAccount);
0205         job->setInfo(info);
0206         job->start();
0207     } else {
0208         slotAttachmentFileDownloadDone(url);
0209     }
0210 }
0211 
0212 void PlaySoundWidget::play()
0213 {
0214     switch (mMediaPlayer->playbackState()) {
0215     case QMediaPlayer::PlayingState:
0216         mMediaPlayer->pause();
0217         break;
0218     default:
0219         mMediaPlayer->play();
0220         break;
0221     }
0222 }
0223 
0224 void PlaySoundWidget::mediaStateChanged(QMediaPlayer::PlaybackState state)
0225 {
0226     switch (state) {
0227     case QMediaPlayer::PlayingState:
0228         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
0229         break;
0230     default:
0231         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0232         break;
0233     }
0234 }
0235 
0236 void PlaySoundWidget::handleError()
0237 {
0238     mPlayButton->setEnabled(false);
0239     const QString errorString = mMediaPlayer->errorString();
0240     QString message = i18n("Error: "); // i18n ?
0241     if (errorString.isEmpty()) {
0242         message += QStringLiteral(" #") + QString::number(int(mMediaPlayer->error()));
0243     } else {
0244         message += errorString;
0245     }
0246     mMessageWidget->setText(message);
0247     mMessageWidget->animatedShow();
0248 }
0249 
0250 #include "moc_playsoundwidget.cpp"