@ -2,7 +2,6 @@
# include <QApplication>
# include <QDebug>
# include <vector>
# include "common/util.h"
# include "selfdrive/ui/qt/request_repeater.h"
@ -12,11 +11,8 @@ static QString shorten(const QString &str, int max_len) {
return str . size ( ) > max_len ? str . left ( max_len ) . trimmed ( ) + " … " : str ;
}
MapSettings : : MapSettings ( bool closeable , QWidget * parent )
: QFrame ( parent ) , current_destination ( nullptr ) {
QSize icon_size ( 100 , 100 ) ;
close_icon = loadPixmap ( " ../assets/icons/close.svg " , icon_size ) ;
MapSettings : : MapSettings ( bool closeable , QWidget * parent ) : QFrame ( parent ) {
close_icon = loadPixmap ( " ../assets/icons/close.svg " , { 100 , 100 } ) ;
setContentsMargins ( 0 , 0 , 0 , 0 ) ;
auto * frame = new QVBoxLayout ( this ) ;
@ -68,7 +64,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent)
current_widget = new DestinationWidget ( this ) ;
QObject : : connect ( current_widget , & DestinationWidget : : actionClicked , [ = ] ( ) {
if ( ! current_destination ) return ;
if ( current_destination . empty ( ) ) return ;
params . remove ( " NavDestination " ) ;
updateCurrentRoute ( ) ;
} ) ;
@ -85,7 +81,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent)
setStyleSheet ( " MapSettings { background-color: #333333; } " ) ;
QObject : : connect ( NavigationRequest : : instance ( ) , & NavigationRequest : : locationsUpdated , this , & MapSettings : : parseResponse ) ;
QObject : : connect ( NavigationRequest : : instance ( ) , & NavigationRequest : : locationsUpdated , this , & MapSettings : : updateLocations ) ;
QObject : : connect ( NavigationRequest : : instance ( ) , & NavigationRequest : : nextDestinationUpdated , this , & MapSettings : : updateCurrentRoute ) ;
}
@ -106,72 +102,37 @@ void MapSettings::updateCurrentRoute() {
qWarning ( ) < < " JSON Parse failed on NavDestination " < < dest ;
return ;
}
auto destination = std : : make_unique < NavDestination > ( doc . object ( ) ) ;
if ( current_destination & & * destination = = * current_destination ) return ;
current_destination = std : : move ( destination ) ;
current_widget - > set ( current_destination . get ( ) , true ) ;
current_destination = doc . object ( ) ;
current_widget - > set ( current_destination , true ) ;
} else {
current_destination . reset ( nullptr ) ;
current_destination = { } ;
current_widget - > unset ( " " , true ) ;
}
if ( isVisible ( ) ) refresh ( ) ;
}
void MapSettings : : parseResponse ( const QString & response , bool success ) {
if ( ! success | | response = = cur_destinations ) return ;
cur_destinations = response ;
void MapSettings : : updateLocations ( const QJsonArray & locations ) {
current_locations = locations ;
refresh ( ) ;
}
void MapSettings : : refresh ( ) {
bool has_home = false , has_work = false ;
auto destinations = std : : vector < std : : unique_ptr < NavDestination > > ( ) ;
auto destinations_str = cur_destinations . trimmed ( ) ;
if ( ! destinations_str . isEmpty ( ) ) {
QJsonDocument doc = QJsonDocument : : fromJson ( destinations_str . toUtf8 ( ) ) ;
if ( doc . isNull ( ) ) {
qWarning ( ) < < " JSON Parse failed on navigation locations " < < cur_destinations ;
return ;
}
for ( auto el : doc . array ( ) ) {
auto destination = std : : make_unique < NavDestination > ( el . toObject ( ) ) ;
// add home and work later if they are missing
if ( destination - > isFavorite ( ) ) {
if ( destination - > label ( ) = = NAV_FAVORITE_LABEL_HOME ) has_home = true ;
else if ( destination - > label ( ) = = NAV_FAVORITE_LABEL_WORK ) has_work = true ;
}
// skip current destination
if ( current_destination & & * destination = = * current_destination ) continue ;
destinations . push_back ( std : : move ( destination ) ) ;
}
}
setUpdatesEnabled ( false ) ;
// TODO: should we build a new layout and swap it in?
clearLayout ( destinations_layout ) ;
// Sort: HOME, WORK, alphabetical FAVORITES, and then most recent (as returned by API)
std : : stable_sort ( destinations . begin ( ) , destinations . end ( ) , [ ] ( const auto & a , const auto & b ) {
if ( a - > isFavorite ( ) & & b - > isFavorite ( ) ) {
if ( a - > label ( ) = = NAV_FAVORITE_LABEL_HOME ) return true ;
else if ( b - > label ( ) = = NAV_FAVORITE_LABEL_HOME ) return false ;
else if ( a - > label ( ) = = NAV_FAVORITE_LABEL_WORK ) return true ;
else if ( b - > label ( ) = = NAV_FAVORITE_LABEL_WORK ) return false ;
else return a - > name ( ) < b - > name ( ) ;
bool has_home = false , has_work = false ;
for ( auto location : current_locations ) {
auto dest = location . toObject ( ) ;
if ( dest [ " save_type " ] . toString ( ) = = NAV_TYPE_FAVORITE ) {
has_home = has_home | | dest [ " label " ] . toString ( ) = = NAV_FAVORITE_LABEL_HOME ;
has_work = has_work | | dest [ " label " ] . toString ( ) = = NAV_FAVORITE_LABEL_WORK ;
}
else if ( a - > isFavorite ( ) ) return true ;
else if ( b - > isFavorite ( ) ) return false ;
return false ;
} ) ;
if ( dest = = current_destination ) continue ;
for ( auto & destination : destinations ) {
auto widget = new DestinationWidget ( this ) ;
widget - > set ( destination . get ( ) , false ) ;
QObject : : connect ( widget , & QPushButton : : clicked , [ this , dest = destination - > toJson ( ) ] ( ) {
widget - > set ( dest , false ) ;
QObject : : connect ( widget , & QPushButton : : clicked , [ this , dest ] ( ) {
navigateTo ( dest ) ;
emit closeSettings ( ) ;
} ) ;
@ -189,11 +150,12 @@ void MapSettings::refresh() {
auto widget = new DestinationWidget ( this ) ;
widget - > unset ( NAV_FAVORITE_LABEL_WORK ) ;
// TODO: refactor to remove this hack
int index = ! has_home | | ( current_destination & & current_destination - > isFavorite ( ) & & current_destination - > label ( ) = = NAV_FAVORITE_LABEL_HOME ) ? 0 : 1 ;
int index = ! has_home | | ( current_destination [ " save_type " ] = = NAV_TYPE_FAVORITE & & current_destination [ " label " ] = = NAV_FAVORITE_LABEL_HOME ) ? 0 : 1 ;
destinations_layout - > insertWidget ( index , widget ) ;
}
destinations_layout - > addStretch ( ) ;
setUpdatesEnabled ( true ) ;
}
void MapSettings : : navigateTo ( const QJsonObject & place ) {
@ -263,23 +225,23 @@ DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) {
) " );
}
void DestinationWidget : : set ( NavDestination * destination , bool current ) {
void DestinationWidget : : set ( const QJsonObject & destination , bool current ) {
setProperty ( " current " , current ) ;
setProperty ( " set " , true ) ;
auto icon_pixmap = current ? icons ( ) . directions : icons ( ) . recent ;
auto title_text = destination - > name ( ) ;
auto subtitle_text = destination - > details ( ) ;
auto title_text = destination [ " place_name " ] . toString ( ) ;
auto subtitle_text = destination [ " place_details " ] . toString ( ) ;
if ( destination - > isFavorite ( ) ) {
if ( destination - > label ( ) = = NAV_FAVORITE_LABEL_HOME ) {
if ( destination [ " save_type " ] = = NAV_TYPE_FAVORITE ) {
if ( destination [ " label " ] = = NAV_FAVORITE_LABEL_HOME ) {
icon_pixmap = icons ( ) . home ;
subtitle_text = title_text + " , " + subtitle_text ;
title_text = tr ( " Home " ) ;
subtitle_text = destination - > name ( ) + " , " + destination - > details ( ) ;
} else if ( destination - > label ( ) = = NAV_FAVORITE_LABEL_WORK ) {
} else if ( destination [ " label " ] = = NAV_FAVORITE_LABEL_WORK ) {
icon_pixmap = icons ( ) . work ;
subtitle_text = title_text + " , " + subtitle_text ;
title_text = tr ( " Work " ) ;
subtitle_text = destination - > name ( ) + " , " + destination - > details ( ) ;
} else {
icon_pixmap = icons ( ) . favorite ;
}
@ -332,9 +294,8 @@ NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) {
// Fetch favorite and recent locations
QString url = CommaApi : : BASE_URL + " /v1/navigation/ " + * dongle_id + " /locations " ;
RequestRepeater * repeater = new RequestRepeater ( this , url , " ApiCache_NavDestinations " , 30 , true ) ;
QObject : : connect ( repeater , & RequestRepeater : : requestDone , this , & NavigationRequest : : locationsUpdated ) ;
QObject : : connect ( repeater , & RequestRepeater : : requestDone , this , & NavigationRequest : : parseLocationsResponse ) ;
}
{
// Destination set while offline
QString url = CommaApi : : BASE_URL + " /v1/navigation/ " + * dongle_id + " /next " ;
@ -358,3 +319,29 @@ NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) {
}
}
}
static void swap ( QJsonValueRef v1 , QJsonValueRef v2 ) { std : : swap ( v1 , v2 ) ; }
void NavigationRequest : : parseLocationsResponse ( const QString & response , bool success ) {
if ( ! success | | response = = prev_response ) return ;
prev_response = response ;
QJsonDocument doc = QJsonDocument : : fromJson ( response . trimmed ( ) . toUtf8 ( ) ) ;
if ( doc . isNull ( ) ) {
qWarning ( ) < < " JSON Parse failed on navigation locations " < < response ;
return ;
}
// Sort: HOME, WORK, alphabetical FAVORITES, and then most recent (as returned by API)
QJsonArray locations = doc . array ( ) ;
std : : stable_sort ( locations . begin ( ) , locations . end ( ) , [ ] ( const QJsonValue & a , const QJsonValue & b ) {
if ( a [ " save_type " ] = = NAV_TYPE_FAVORITE | | b [ " save_type " ] = = NAV_TYPE_FAVORITE ) {
QString a_label = a [ " label " ] . toString ( ) , b_label = b [ " label " ] . toString ( ) ;
return std : : tuple ( a [ " save_type " ] . toString ( ) , ( a_label . isEmpty ( ) ? " xxx " : a_label ) , a [ " place_name " ] . toString ( ) ) <
std : : tuple ( b [ " save_type " ] . toString ( ) , ( b_label . isEmpty ( ) ? " xxx " : b_label ) , b [ " place_name " ] . toString ( ) ) ;
} else {
return false ;
}
} ) ;
emit locationsUpdated ( locations ) ;
}