Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/app/arbiter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,5 @@ class Arbiter : public QObject {
void cursor_changed(bool enabled);
void action_changed(Action *action, QString key);
void openauto_full_screen(bool fullscreen);
void openauto_connection_changed(bool connected);
};
78 changes: 78 additions & 0 deletions include/app/quick_views/media.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDial>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
#include <QFrame>
#include "app/quick_views/quick_view.hpp"
#include "AAHandler.hpp"

class Arbiter;
class MediaWidget;

class MediaQuickView : public QFrame, public QuickView {
Q_OBJECT

public:
MediaQuickView(Arbiter &arbiter);
QWidget *centralwidget;
QHBoxLayout *horizontalLayout;
QLabel *album_art_label;
QWidget *widget_2;
QVBoxLayout *verticalLayout_2;
QWidget *widget_3;
QHBoxLayout *horizontalLayout_3;
QSpacerItem *horizontalSpacer_5;
QPushButton *prev_button;
QPushButton *play_button;
QPushButton *next_button;
QSpacerItem *horizontalSpacer_6;
QLabel *track_progress_label;
QLabel *title_label;
QProgressBar *progressBar;
MediaWidget *mediaWidget;

void init() override;

};

class MediaWidget : public QWidget
{
Q_OBJECT

public:
MediaWidget(Arbiter &arbiter, QWidget *parent = 0);
void clear();

public slots:
void updateMetadata(const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata);
void updatePlayback(const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback);

protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
Arbiter &arbiter;
aasdk::proto::messages::MediaInfoChannelMetadataData metadata_;
aasdk::proto::messages::MediaInfoChannelPlaybackData playback_;
bool connected = false;
QPoint title_pos;
bool scroll_text_left = true;
QTimer *scroll_timer;
QRect play_rect;
QRect prev_rect;
QRect next_rect;
};




6 changes: 4 additions & 2 deletions src/app/pages/openauto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ OpenAutoWorker::OpenAutoWorker(std::function<void(bool)> callback, bool night_mo
{
this->create_usb_workers();
this->create_io_service_workers();

this->app->waitForDevice(true);
AAHandler *aa_handler = arbiter.android_auto().handler;
service_factory.setAndroidAutoInterface(aa_handler);
Expand Down Expand Up @@ -427,8 +427,10 @@ void OpenAutoPage::init()

std::function<void(bool)> callback = [frame = this->frame](bool active) { frame->toggle(active); };
this->worker = new OpenAutoWorker(callback, this->arbiter.theme().mode == Session::Theme::Dark, frame, this->arbiter);

connect(this->frame, &OpenAutoFrame::toggle, [this](bool enable){
emit arbiter.openauto_connection_changed(enable);

if (!enable && this->frame->is_fullscreen()) {
this->addWidget(frame);
this->frame->toggle_fullscreen();
Expand Down
226 changes: 226 additions & 0 deletions src/app/quick_views/media.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include <QFrame>
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDial>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
#include <QDebug>

#include "app/arbiter.hpp"
#include "app/widgets/dialog.hpp"
#include "app/quick_views/media.hpp"
#include "app/utilities/icon_engine.hpp"

MediaQuickView::MediaQuickView(Arbiter &arbiter)
: QFrame()
, QuickView(arbiter, "Media", this)
{

}

void MediaQuickView::init(){

AAHandler *aa_handler = arbiter.android_auto().handler;
mediaWidget = new MediaWidget(this->arbiter,this);

horizontalLayout = new QHBoxLayout(this);
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
horizontalLayout->setContentsMargins(0, 0, 0, 0);
mediaWidget = new MediaWidget(this->arbiter, this);
horizontalLayout->addWidget(mediaWidget);

connect(aa_handler, &AAHandler::aa_media_playback_update, [this](const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback){
mediaWidget->updatePlayback(playback);
});

connect(aa_handler, &AAHandler::aa_media_metadata_update, [this](const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata){
mediaWidget->updateMetadata(metadata);
});

}

MediaWidget::MediaWidget(Arbiter &arbiter, QWidget *parent)
: QWidget(parent)
,arbiter(arbiter)
{

scroll_timer = new QTimer(this);
scroll_timer->start(33);//30fps
connect(scroll_timer, &QTimer::timeout, [this](){

if(metadata_.has_track_name() && metadata_.has_artist_name()){
QString track_info(QString::fromStdString(metadata_.artist_name()) +
" - " +
QString::fromStdString(metadata_.track_name()));
QFont myFont("arial", 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id prefer if you used Session::Forge::font just to keep everything consistent and could have the size adjust based on a user's scale

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the topic of scaling - is there going to be an issue with the hard coded constants I see?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do! I'll also be sure to change it so the track information is centered according to the font metrics instead of the 5px offset like I have now. @icecube45 what do you mean by hard coded constants? Besides the font, I'm pretty sure everything is set in relation to the height and width of the widget, except the padding of course. If the user adjusts the scale, the height of the control bar changes because of the power buttons to the right, the MediaWidget draws the icons according to the height of the control bar so it adjusts to the scale automatically. I've attached some screenshots taken with a large and small scale applied. Let me know if I'm missing something else tho!
large-scale
small-scale

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring to things like the QSize(128,128) or drawArc(play_rect,1440,5760);, but im not too concerned

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was a bit concerned when i saw that first but it seems like everything is done relative to the widgets size so things should size appropriately (the qsize doesnt matter much here since the painter determines the size and those constants there are just for the arc's angle so all good there)

QFontMetrics fm(myFont);
int text_width=fm.width(track_info);

int text_rect_width = (width() / 2) - (height() / 2 + (height() * 2));

if(text_width > text_rect_width){

if(scroll_text_left){
title_pos.setX(title_pos.x() - 1);
if((title_pos.x() + text_width) < height() + text_rect_width){
scroll_text_left = false;
}

} else{
title_pos.setX(title_pos.x() + 1);
if(title_pos.x() > height()){
scroll_text_left = true;
}
}

}

}

this->update();

});

connect(&arbiter, &Arbiter::openauto_connection_changed, [this](bool connected){

this->connected = connected;

});

}

void MediaWidget::paintEvent(QPaintEvent *event)
{

// Setup
QPainter painter(this);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
if(arbiter.theme().mode == Session::Theme::Mode::Dark){
painter.setBackground(QBrush(QColor(48,48,48)));
painter.setPen(QPen(Qt::white,1));
} else {
painter.setBackground(QBrush(QColor(250,250,250)));
painter.setPen(QPen(Qt::black,1));
}

painter.eraseRect(0,0,width(),height());

/*** ALBUM ART ***/
if(this->metadata_.has_album_art() && connected){

QImage art;
art.loadFromData(QByteArray::fromStdString(metadata_.album_art()));
QSize size = QSize(height(), height());
auto scaled = art.scaled(size, Qt::IgnoreAspectRatio, Qt::FastTransformation);
painter.drawImage(QPoint(0,0),scaled);

}

/*** TRACK TITLE ***/
int text_rect_width = (width() / 2) - (height() / 2 + (height() * 2));
if(metadata_.has_track_name() && metadata_.has_artist_name() && connected){
QString track_info(QString::fromStdString(metadata_.artist_name()) +
" - " +
QString::fromStdString(metadata_.track_name()));
QFont myFont("arial", 10);
painter.setFont(myFont);
painter.setClipRect(height(),0, text_rect_width ,height());
painter.drawText(title_pos, track_info);
painter.setClipping(false);
}

/*** PREV BUTTON ***/
QIcon prev_icon(new IconEngine(this->arbiter, QString("://icons/skip_previous.svg"), true));
QPixmap prev_pixmap = prev_icon.pixmap(QSize(128,128));
painter.drawPixmap(prev_rect,prev_pixmap);

/*** PLAY/PAUSE BUTTON ***/
if(this->playback_.has_track_progress() && connected &&
metadata_.has_track_length() && metadata_.track_length() != 0){

QPen progressBGPen = QPen(arbiter.theme().color(),4);
painter.setPen(progressBGPen);
painter.drawArc(play_rect,1440,5760);
QPen progressPen = QPen(QColor(70,70,70),4);
painter.setPen(progressPen);
int step = 5760 / metadata_.track_length();
int invVal = metadata_.track_length() - playback_.track_progress();
int val = step * invVal;
painter.drawArc(play_rect,1440,val);

} else {
QPen progressPen = QPen(QColor(70,70,70),4);
painter.setPen(progressPen);
painter.drawArc(play_rect,1440,5760);
}

if(playback_.has_playback_state() && playback_.playback_state() == aasdk::proto::messages::MediaInfoChannelPlaybackData_PlaybackState_PLAY){
QIcon icon(new IconEngine(this->arbiter, QString("://icons/pause.svg"), true));
QPixmap pause = icon.pixmap(QSize(128,128));
painter.drawPixmap(play_rect,pause);

} else {
QIcon icon(new IconEngine(this->arbiter, QString("://icons/play.svg"), true));
QPixmap play = icon.pixmap(QSize(128,128));
painter.drawPixmap(play_rect,play);
}

/**** NEXT BUTTON ****/
QIcon next_icon(new IconEngine(this->arbiter, QString("://icons/skip_next.svg"), true));
QPixmap next_pixmap = next_icon.pixmap(QSize(128,128));
painter.drawPixmap(next_rect,next_pixmap);

}

void MediaWidget::resizeEvent(QResizeEvent *event)
{
prev_rect = QRect((width() / 2) - (height() / 2 + height()), 8,height() - 16,height() - 16);
play_rect = QRect((width() / 2) - (height() / 2), 8,height() - 16,height() - 16);
next_rect = QRect((width() / 2) + (height() / 2), 8,height() - 16,height() - 16);

}

void MediaWidget::mousePressEvent(QMouseEvent *event)
{

if(event->button()==Qt::LeftButton)
{

AAHandler *aa_handler = arbiter.android_auto().handler;
if(prev_rect.contains(event->pos())){
aa_handler->injectButtonPress(aasdk::proto::enums::ButtonCode::PREV,openauto::projection::ButtonEventType::RELEASE);
}
if(play_rect.contains(event->pos())){
aa_handler->injectButtonPress(aasdk::proto::enums::ButtonCode::TOGGLE_PLAY,openauto::projection::ButtonEventType::PRESS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I had made a helper method that sent both press and release events for you, but I can't find where that ended up (if it made it in at all), but might be something to double check

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's the AAHandler::injectButtonPressHelper, is that what you're talking about?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, no, that's for the event filter stuff.

aa_handler->injectButtonPress(aasdk::proto::enums::ButtonCode::TOGGLE_PLAY,openauto::projection::ButtonEventType::RELEASE);
}
if(next_rect.contains(event->pos())){
aa_handler->injectButtonPress(aasdk::proto::enums::ButtonCode::NEXT,openauto::projection::ButtonEventType::RELEASE);
}

}
}

void MediaWidget::updatePlayback(const aasdk::proto::messages::MediaInfoChannelPlaybackData& playback)
{

this->playback_ = playback;
this->update();

}

void MediaWidget::updateMetadata(const aasdk::proto::messages::MediaInfoChannelMetadataData& metadata)
{

title_pos = QPoint(height() + 8, (height() / 2) + 5);//reset
this->metadata_ = metadata;
this->update();

}


4 changes: 3 additions & 1 deletion src/app/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "aasdk_proto/ButtonCodeEnum.pb.h"

#include "app/session.hpp"
#include "app/quick_views/media.hpp"

QDir Session::plugin_dir(QString plugin)
{
Expand Down Expand Up @@ -89,7 +90,8 @@ Session::Layout::ControlBar::ControlBar(QSettings &settings, Arbiter &arbiter)
new NullQuickView(arbiter),
new VolumeQuickView(arbiter),
new BrightnessQuickView(arbiter),
new ComboQuickView(arbiter)
new ComboQuickView(arbiter),
new MediaQuickView(arbiter)
};

this->curr_quick_view = this->quick_views_.value(settings.value("Layout/ControlBar/quick_view", 0).toInt());
Expand Down
2 changes: 0 additions & 2 deletions src/app/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ QWidget *Dash::control_bar() const
quick_views->setCurrentWidget(quick_view->widget());
});

layout->addStretch();
Copy link
Contributor

@rsjudka rsjudka May 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so removing this breaks the layout for the other quick views... I'm assuming you were trying to get everything centered on the control bar? The stretch was added here to have everything aligned to the left (to keep it distinct from the close+power buttons) so i think it should stay

since it looks like you're dealing with all the painting of the widget yourself, you'll probs need to handle sizing your widget (overriding sizeHint, etc) so the layout essentially knows to give it as much space as possible

or you might also be able to play with the layout in MediaQuickView::init (maybe)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah partly because I thought it looked better centered, and partly to give more room for the track details before it reverts to scrolling the information. I wasn't really sure why it was in there but I can certainly put it back and use a sizeHint to get the desired look without breaking the layout for other quick views.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rsjudka I've experimented with a couple different methods of changing the size of the media controls to override the default using sizeHints and sizePolicies and different stretch factors but nothing really works properly, it really wants to keep the default size of half the bar. I was thinking I could set it according to the parent size but the parent size changes after initialisation and the sizeHint is constant. The closest I could get was to set a huge width in the sizeHint so it would shrink into the proper width, but even then the height was hard to set right and wouldn't scale properly. None of this felt right either lol. IMO the proper approach would be to add a stretch inside of the quick_views where we want that separation from the power controls. Alternatively I could simply draw the prev, play, and next button at the far right of the area given to the Media Control, but the spacing won't be quite centred. The reason I'd personally prefer the former though is because I'm also working on a separate feature that would allow vehicle plugins to dynamically add quick views, which I think would be useful to plugin developers if they wanted to add, as an example, a HVAC control quick view for their particular vehicle. Having the full area available to developers would be desirable IMO. Let me know which approach you'd prefer to implement though, either one is fairly simple.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah sorry my first recommendation would have been a bit more involved to get things sized for your quick view, but i think adding the ability to add quick views dynamically sounds awesome (and would definitely benefit from having the control bar's layout more adaptable)

as to not break the current quick views when this gets merged, could you go ahead and test moving the stretch to the individual quick views?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to get back to you so late @rsjudka, life's been busy so I haven't had much time to work on this. I did implement and test the changes as stated and it works as intended. I'll try and get to pushing the changes today. I do have the dynamic quick views implemented in another local branch too, which I've been using for my particular application where I inject some HVAC controls from the vehicle's plugin. If you're interested I can merge those into this branch too, or I can create a separate PR, whichever you'd prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries :) i think as a separate pr would be great so we can get merged in as soon as youre ready with it!


auto dialog = new Dialog(this->arbiter, true, this->arbiter.window());
dialog->set_title("Power Off");
dialog->set_body(this->power_control());
Expand Down