├── .gitignore ├── docs └── imgs │ ├── data.png │ ├── home.png │ ├── media.png │ ├── settings.png │ ├── openauto_maps.png │ └── openauto_spotify.png ├── assets ├── fonts │ ├── Montserrat │ │ ├── Montserrat-Black.ttf │ │ ├── Montserrat-Bold.ttf │ │ ├── Montserrat-Light.ttf │ │ ├── Montserrat-Thin.ttf │ │ ├── Montserrat-Italic.ttf │ │ ├── Montserrat-Medium.ttf │ │ ├── Montserrat-Regular.ttf │ │ ├── Montserrat-BoldItalic.ttf │ │ ├── Montserrat-ExtraBold.ttf │ │ ├── Montserrat-ExtraLight.ttf │ │ ├── Montserrat-SemiBold.ttf │ │ ├── Montserrat-ThinItalic.ttf │ │ ├── Montserrat-BlackItalic.ttf │ │ ├── Montserrat-LightItalic.ttf │ │ ├── Montserrat-MediumItalic.ttf │ │ ├── Montserrat-ExtraBoldItalic.ttf │ │ ├── Montserrat-SemiBoldItalic.ttf │ │ ├── Montserrat-ExtraLightItalic.ttf │ │ └── OFL.txt │ └── Titillium_Web │ │ ├── TitilliumWeb-Black.ttf │ │ ├── TitilliumWeb-Bold.ttf │ │ ├── TitilliumWeb-Light.ttf │ │ ├── TitilliumWeb-Italic.ttf │ │ ├── TitilliumWeb-Regular.ttf │ │ ├── TitilliumWeb-BoldItalic.ttf │ │ ├── TitilliumWeb-ExtraLight.ttf │ │ ├── TitilliumWeb-SemiBold.ttf │ │ ├── TitilliumWeb-LightItalic.ttf │ │ ├── TitilliumWeb-SemiBoldItalic.ttf │ │ ├── TitilliumWeb-ExtraLightItalic.ttf │ │ └── OFL.txt ├── icons │ ├── light │ │ ├── play-24px.svg │ │ ├── volume_mute-24px.svg │ │ ├── pause-24px.svg │ │ ├── skip_next-24px.svg │ │ ├── skip_previous-24px.svg │ │ ├── fast_forward-24px.svg │ │ ├── fast_rewind-24px.svg │ │ ├── arrow_drop_down-24px.svg │ │ ├── check-24px.svg │ │ ├── report_problem-24px.svg │ │ ├── tune-24px.svg │ │ ├── save-24px.svg │ │ ├── play_circle_outline-24px.svg │ │ ├── brightness_low-24px.svg │ │ ├── volume_up-24px.svg │ │ ├── brightness_high-24px.svg │ │ ├── speed-24px.svg │ │ ├── directions_car-24px.svg │ │ ├── volume_off-24px.svg │ │ ├── bluetooth_searching-24px.svg │ │ └── build-24px.svg │ └── dark │ │ ├── play-24px.svg │ │ ├── volume_mute-24px.svg │ │ ├── pause-24px.svg │ │ ├── skip_next-24px.svg │ │ ├── skip_previous-24px.svg │ │ ├── fast_forward-24px.svg │ │ ├── fast_rewind-24px.svg │ │ ├── arrow_drop_down-24px.svg │ │ ├── check-24px.svg │ │ ├── report_problem-24px.svg │ │ ├── tune-24px.svg │ │ ├── save-24px.svg │ │ ├── play_circle_outline-24px.svg │ │ ├── brightness_low-24px.svg │ │ ├── volume_up-24px.svg │ │ ├── brightness_high-24px.svg │ │ ├── speed-24px.svg │ │ ├── volume_off-24px.svg │ │ ├── directions_car-24px.svg │ │ └── bluetooth_searching-24px.svg ├── resources.qrc └── stylesheets │ ├── light.qss │ └── dark.qss ├── .gitmodules ├── include ├── obd │ ├── conversions.hpp │ ├── decoders.hpp │ ├── command.hpp │ ├── obd.hpp │ └── message.hpp └── app │ ├── widgets │ ├── tuner.hpp │ ├── progress.hpp │ └── switch.hpp │ ├── tabs │ ├── media.hpp │ ├── data.hpp │ ├── settings.hpp │ └── open_auto.hpp │ ├── window.hpp │ ├── bluetooth.hpp │ ├── config.hpp │ └── theme.hpp ├── src ├── obd │ ├── command.cpp │ └── obd.cpp ├── dash.cpp └── app │ ├── widgets │ ├── tuner.cpp │ ├── switch.cpp │ └── progress.cpp │ ├── config.cpp │ ├── bluetooth.cpp │ ├── tabs │ ├── open_auto.cpp │ ├── data.cpp │ ├── media.cpp │ └── settings.cpp │ ├── window.cpp │ └── theme.cpp ├── cmake_modules ├── Findrtaudio.cmake └── Findlibusb-1.0.cmake ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | bin/ 3 | build/ 4 | 5 | CMakeLists.txt.user 6 | -------------------------------------------------------------------------------- /docs/imgs/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/data.png -------------------------------------------------------------------------------- /docs/imgs/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/home.png -------------------------------------------------------------------------------- /docs/imgs/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/media.png -------------------------------------------------------------------------------- /docs/imgs/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/settings.png -------------------------------------------------------------------------------- /docs/imgs/openauto_maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/openauto_maps.png -------------------------------------------------------------------------------- /docs/imgs/openauto_spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/docs/imgs/openauto_spotify.png -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Thin.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-ThinItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-BlackItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-LightItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-MediumItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsjudka/intelligent-auto/HEAD/assets/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /assets/icons/light/play-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/play-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "aasdk"] 2 | path = aasdk 3 | url = https://github.com/rsjudka/aasdk.git 4 | branch = master 5 | [submodule "openauto"] 6 | path = openauto 7 | url = https://github.com/rsjudka/openauto.git 8 | branch = master 9 | -------------------------------------------------------------------------------- /assets/icons/light/volume_mute-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/volume_mute-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/pause-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/pause-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/skip_next-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/skip_previous-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/skip_next-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/skip_previous-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/fast_forward-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/fast_rewind-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/fast_forward-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/fast_rewind-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/arrow_drop_down-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/arrow_drop_down-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/check-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/check-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/report_problem-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/report_problem-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/tune-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/tune-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/save-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/save-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /include/obd/conversions.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONVERSIONS_HPP_ 2 | #define CONVERSIONS_HPP_ 3 | 4 | double kph_to_mph(double val) { return val * 0.621371; } 5 | 6 | double c_to_f(double val) { return (val * 9 / 5) + 32; } 7 | 8 | double gps_to_gph(double val) { return val * 0.0805; } 9 | 10 | double gps_to_lph(double val) { return val * 3.6; } 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /assets/icons/light/play_circle_outline-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/play_circle_outline-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/brightness_low-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/brightness_low-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/volume_up-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/volume_up-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/brightness_high-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/brightness_high-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/speed-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/icons/dark/speed-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /include/obd/decoders.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DECODERS_HPP_ 2 | #define DECODERS_HPP_ 3 | 4 | #include 5 | 6 | double percentage(Response resp) { return (100.0 / 255.0) * resp.A; } 7 | 8 | double temp(Response resp) { return resp.A - 40; } 9 | 10 | double rpm(Response resp) { return ((256.0 * resp.A) + resp.B) / 4.0; } 11 | 12 | double speed(Response resp) { return resp.A; } 13 | 14 | double flow(Response resp) { return ((256.0 * resp.A) + resp.B) / 100.0; } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/obd/command.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_HPP_ 2 | #define COMMAND_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct Command { 10 | std::string description; 11 | Request request; 12 | std::function decoder; 13 | }; 14 | 15 | struct Commands { 16 | Command LOAD; 17 | Command COOLANT_TEMP; 18 | Command RPM; 19 | Command SPEED; 20 | Command INTAKE_TEMP; 21 | Command MAF; 22 | }; 23 | 24 | extern Commands cmds; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/obd/command.cpp: -------------------------------------------------------------------------------- 1 | #include "obd/command.hpp" 2 | #include "obd/decoders.hpp" 3 | 4 | Commands cmds = {{"Calculated Engine Load", Request(0x01, 0x04), percentage}, 5 | {"Engine Coolant Temperature", Request(0x01, 0x05), temp}, 6 | {"Engine Revolutions Per Minute (RPM)", Request(0x01, 0x0C), rpm}, 7 | {"Vehicle Speed", Request(0x01, 0x0D), speed}, 8 | {"Intake Air Temperature", Request(0x01, 0x0F), temp}, 9 | {"Mass Air Flow (MAF) Rate", Request(0x01, 0x10), flow}}; 10 | -------------------------------------------------------------------------------- /assets/icons/light/directions_car-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/icons/light/volume_off-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/volume_off-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/directions_car-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/icons/light/bluetooth_searching-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/dark/bluetooth_searching-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/light/build-24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /include/app/widgets/tuner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TUNER_HPP_ 2 | #define TUNER_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | class Tuner : public QSlider { 9 | Q_OBJECT 10 | Q_PROPERTY(QColor color READ get_color WRITE set_color) 11 | 12 | public: 13 | Tuner(QWidget *parent = nullptr); 14 | 15 | QSize sizeHint() const override; 16 | 17 | QColor get_color() { return this->color; } 18 | void set_color(QColor color) { this->color = color; } 19 | 20 | protected: 21 | void paintEvent(QPaintEvent *event) override; 22 | 23 | signals: 24 | void station_updated(int); 25 | 26 | private: 27 | Config *config; 28 | QColor color; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/dash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QApplication dash(argc, argv); 9 | QStringList args = QCoreApplication::arguments(); 10 | 11 | MainWindow window; 12 | window.setWindowFlags(Qt::FramelessWindowHint); 13 | if (args.size() > 2) { 14 | int w = args.at(1).toInt(); 15 | int h = args.at(2).toInt(); 16 | window.setFixedSize(w, h); 17 | } 18 | else { 19 | dash.setOverrideCursor(Qt::BlankCursor); 20 | window.setFixedSize(dash.desktop()->screenGeometry().size()); 21 | } 22 | window.move(0, 0); 23 | window.show(); 24 | 25 | return dash.exec(); 26 | } 27 | -------------------------------------------------------------------------------- /include/obd/obd.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OBD_HPP_ 2 | #define OBD_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class OBD { 12 | public: 13 | OBD(); 14 | ~OBD(); 15 | 16 | bool query(Command &cmd, double &val); 17 | 18 | inline bool is_connected() { return this->connected; } 19 | 20 | static OBD *get_instance(); 21 | 22 | private: 23 | void connect(std::string dev_path, speed_t baudrate); 24 | void initialize(); 25 | bool send(Request req); 26 | int _write(std::string str); 27 | bool is_failed_response(std::string str); 28 | Response receive(); 29 | std::string _read(); 30 | 31 | inline std::string raw_query(std::string cmd) { return (this->_write(cmd) > 0) ? this->_read() : ""; } 32 | 33 | int fd; 34 | std::mutex obd_mutex; 35 | 36 | bool connected = false; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/app/tabs/media.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MEDIA_HPP_ 2 | #define MEDIA_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class MediaTab : public QTabWidget { 12 | Q_OBJECT 13 | 14 | public: 15 | MediaTab(QWidget *parent = nullptr); 16 | }; 17 | 18 | class BluetoothPlayerSubTab : public QWidget { 19 | Q_OBJECT 20 | 21 | public: 22 | BluetoothPlayerSubTab(QWidget *parent = nullptr); 23 | 24 | private: 25 | QWidget *track_widget(); 26 | QWidget *controls_widget(); 27 | 28 | Bluetooth *bluetooth; 29 | Theme *theme; 30 | }; 31 | 32 | class RadioPlayerSubTab : public QWidget { 33 | Q_OBJECT 34 | 35 | public: 36 | RadioPlayerSubTab(QWidget *parent = nullptr); 37 | 38 | private: 39 | QWidget *tuner_widget(); 40 | QWidget *controls_widget(); 41 | 42 | Config *config; 43 | Theme *theme; 44 | Tuner *tuner; 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/app/window.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_HPP_ 2 | #define WINDOW_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class MainWindow : public QMainWindow { 18 | Q_OBJECT 19 | 20 | public: 21 | MainWindow(); 22 | 23 | protected: 24 | void showEvent(QShowEvent *event); 25 | 26 | private: 27 | static void update_system_volume(int position); 28 | QTabWidget *tabs_widget(); 29 | QWidget *controls_widget(); 30 | QWidget *volume_widget(); 31 | 32 | Config *config; 33 | Theme *theme; 34 | OpenAutoTab *open_auto_tab; 35 | 36 | signals: 37 | void set_data_state(bool enabled); 38 | void set_open_auto_state(unsigned int alpha); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /include/app/widgets/progress.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROGRESS_H 2 | #define PROGRESS_H 3 | 4 | #include 5 | #include 6 | 7 | class ProgressIndicator : public QWidget { 8 | Q_OBJECT 9 | Q_PROPERTY(qreal dash_offset READ get_dash_offset WRITE set_dash_offset) 10 | Q_PROPERTY(qreal dash_length READ get_dash_length WRITE set_dash_length) 11 | Q_PROPERTY(int angle READ get_angle WRITE set_angle) 12 | 13 | public: 14 | ProgressIndicator(QWidget* parent = nullptr); 15 | 16 | inline int get_angle() { return this->angle; } 17 | inline void set_angle(int angle) { this->angle = angle; } 18 | 19 | inline qreal get_dash_length() { return this->dash_length; } 20 | inline void set_dash_length(qreal length) { this->dash_length = length; } 21 | 22 | inline qreal get_dash_offset() { return this->dash_offset; } 23 | inline void set_dash_offset(qreal offset) { this->dash_offset = offset; } 24 | 25 | public slots: 26 | void startAnimation(); 27 | void stopAnimation(); 28 | 29 | protected: 30 | virtual inline void timerEvent(QTimerEvent* event) { update(); } 31 | virtual void paintEvent(QPaintEvent* event); 32 | 33 | private: 34 | int timer_id = -1; 35 | bool enabled = false; 36 | int angle = 0; 37 | qreal dash_length = 24; 38 | qreal dash_offset = 0; 39 | }; 40 | 41 | #endif -------------------------------------------------------------------------------- /include/app/tabs/data.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DATA_HPP_ 2 | #define DATA_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | typedef std::function, bool)> obd_decoder_t; 10 | typedef QPair units_t; 11 | 12 | class Gauge : public QWidget { 13 | Q_OBJECT 14 | 15 | public: 16 | enum Orientation { BOTTOM, RIGHT }; 17 | 18 | Gauge(units_t units, QFont value_font, QFont unit_font, Orientation orientation, int rate, 19 | std::vector cmds, int precision, obd_decoder_t decoder, QWidget *parent = nullptr); 20 | 21 | inline void start() { this->timer->start(this->rate); } 22 | inline void stop() { this->timer->stop(); } 23 | 24 | private: 25 | QString format_value(double value); 26 | QString null_value(); 27 | 28 | bool si; 29 | int rate; 30 | int precision; 31 | QTimer *timer; 32 | 33 | signals: 34 | void toggle_unit(bool si); 35 | }; 36 | 37 | class DataTab : public QWidget { 38 | Q_OBJECT 39 | 40 | public: 41 | DataTab(QWidget *parent = nullptr); 42 | 43 | private: 44 | QWidget *cluster_widget(); 45 | QWidget *driving_data_widget(); 46 | QWidget *speedo_tach_widget(); 47 | QWidget *mileage_data_widget(); 48 | QWidget *engine_data_widget(); 49 | QWidget *coolant_temp_widget(); 50 | QWidget *engine_load_widget(); 51 | 52 | std::vector gauges; 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/obd/message.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGE_HPP_ 2 | #define MESSAGE_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct Message { 9 | unsigned char mode; 10 | unsigned char PID; 11 | }; 12 | 13 | struct Request : Message { 14 | Request(unsigned char mode, unsigned char PID) 15 | { 16 | this->mode = mode; 17 | this->PID = PID; 18 | } 19 | 20 | std::string to_str() 21 | { 22 | std::stringstream ss; 23 | ss << std::hex << std::setfill('0'); 24 | ss << std::setw(2) << static_cast(this->mode) << std::setw(2) << static_cast(this->PID); 25 | return ss.str(); 26 | } 27 | }; 28 | 29 | struct Response : Message { 30 | bool success = true; 31 | unsigned char A; 32 | unsigned char B; 33 | unsigned char C; 34 | unsigned char D; 35 | 36 | Response(std::string resp_str) 37 | { 38 | this->mode = std::stoi(resp_str.substr(0, 2), nullptr, 16); 39 | this->PID = std::stoi(resp_str.substr(2, 2), nullptr, 16); 40 | this->A = std::stoi(resp_str.substr(4, 2), nullptr, 16); 41 | if (resp_str.length() >= 8) this->B = std::stoi(resp_str.substr(6, 2), nullptr, 16); 42 | if (resp_str.length() >= 10) this->C = std::stoi(resp_str.substr(8, 2), nullptr, 16); 43 | if (resp_str.length() == 12) this->D = std::stoi(resp_str.substr(10, 2), nullptr, 16); 44 | } 45 | 46 | Response() { this->success = false; } 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/app/widgets/switch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SWITCH_HPP_ 2 | #define SWITCH_HPP_ 3 | 4 | #include 5 | 6 | class Switch : public QAbstractButton { 7 | Q_OBJECT 8 | Q_PROPERTY(QColor track_color READ get_track_color WRITE set_track_color) 9 | Q_PROPERTY(QColor thumb_color READ get_thumb_color WRITE set_thumb_color) 10 | Q_PROPERTY(int offset READ get_offset WRITE set_offset) 11 | 12 | public: 13 | Switch(QWidget *parent = nullptr); 14 | 15 | QSize sizeHint() const override; 16 | 17 | QColor get_track_color() { return this->track_color; }; 18 | QColor get_thumb_color() { return this->thumb_color; }; 19 | int get_offset() { return this->offset; } 20 | 21 | void set_track_color(QColor track_color) { this->track_color = track_color; } 22 | void set_thumb_color(QColor thumb_color) { this->thumb_color = thumb_color; } 23 | void set_offset(int offset) { this->offset = offset; } 24 | 25 | protected: 26 | void paintEvent(QPaintEvent *event) override; 27 | void resizeEvent(QResizeEvent *event) override; 28 | void mouseReleaseEvent(QMouseEvent *event) override; 29 | void nextCheckState() override; 30 | 31 | private: 32 | int track_radius; 33 | int thumb_radius; 34 | 35 | QColor track_color = Qt::transparent; 36 | QColor thumb_color = Qt::transparent; 37 | int offset; 38 | int margin; 39 | int base_offset; 40 | std::function end_offset; 41 | 42 | signals: 43 | void stateChanged(bool); 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/app/bluetooth.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BLUETOOTH_HPP_ 2 | #define BLUETOOTH_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | class Bluetooth : public QObject { 19 | Q_OBJECT 20 | 21 | public: 22 | Bluetooth(); 23 | 24 | void scan(); 25 | 26 | inline QList get_devices() 27 | { 28 | return this->has_adapter() ? this->adapter->devices() : QList(); 29 | } 30 | inline QPair get_media_player() 31 | { 32 | if (this->media_player_device != nullptr) 33 | return {this->media_player_device->name(), this->media_player_device->mediaPlayer()}; 34 | 35 | return {QString(), nullptr}; 36 | } 37 | inline bool has_adapter() { return this->adapter != nullptr; } 38 | 39 | static Bluetooth *get_instance(); 40 | 41 | private: 42 | void update_media_player(BluezQt::DevicePtr device); 43 | 44 | BluezQt::AdapterPtr adapter; 45 | BluezQt::DevicePtr media_player_device; 46 | 47 | signals: 48 | void device_added(BluezQt::DevicePtr); 49 | void device_changed(BluezQt::DevicePtr); 50 | void device_removed(BluezQt::DevicePtr); 51 | void media_player_changed(QString, BluezQt::MediaPlayerPtr); 52 | void media_player_status_changed(BluezQt::MediaPlayer::Status); 53 | void media_player_track_changed(BluezQt::MediaPlayerTrack); 54 | void scan_status(bool); 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/app/tabs/settings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_HPP_ 2 | #define SETTINGS_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | class SettingsTab : public QTabWidget { 12 | Q_OBJECT 13 | 14 | public: 15 | SettingsTab(QWidget *parent = nullptr); 16 | }; 17 | 18 | class GeneralSettingsSubTab : public QWidget { 19 | Q_OBJECT 20 | 21 | public: 22 | GeneralSettingsSubTab(QWidget *parent = nullptr); 23 | 24 | private: 25 | QWidget *settings_widget(); 26 | QWidget *dark_mode_row_widget(); 27 | QWidget *brightness_row_widget(); 28 | QWidget *brightness_widget(); 29 | QWidget *si_units_row_widget(); 30 | QWidget *color_row_widget(); 31 | QWidget *controls_widget(); 32 | 33 | Config *config; 34 | Theme *theme; 35 | }; 36 | 37 | class BluetoothSettingsSubTab : public QWidget { 38 | Q_OBJECT 39 | 40 | public: 41 | BluetoothSettingsSubTab(QWidget *parent = nullptr); 42 | 43 | private: 44 | QWidget *controls_widget(); 45 | QWidget *scanner_widget(); 46 | QWidget *devices_widget(); 47 | 48 | Bluetooth *bluetooth; 49 | Config *config; 50 | Theme *theme; 51 | QMap devices; 52 | }; 53 | 54 | class OpenAutoSettingsSubTab : public QWidget { 55 | Q_OBJECT 56 | 57 | public: 58 | OpenAutoSettingsSubTab(QWidget *parent = nullptr); 59 | 60 | private: 61 | QWidget *settings_widget(); 62 | QWidget *rhd_row_widget(); 63 | QWidget *frame_rate_row_widget(); 64 | QWidget *resolution_row_widget(); 65 | QWidget *dpi_row_widget(); 66 | QWidget *dpi_widget(); 67 | QWidget *rt_audio_row_widget(); 68 | QWidget *audio_channels_row_widget(); 69 | QWidget *bluetooth_row_widget(); 70 | 71 | Bluetooth *bluetooth; 72 | Config *config; 73 | Theme *theme; 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/app/widgets/tuner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | Tuner::Tuner(QWidget *parent) : QSlider(Qt::Orientation::Horizontal, parent) 8 | { 9 | this->config = Config::get_instance(); 10 | 11 | setRange(880, 1080); 12 | setTickInterval(1); 13 | setSliderPosition(this->config->get_radio_station()); 14 | 15 | connect(this, &QSlider::valueChanged, [this](int value) { 16 | this->config->set_radio_station(value); 17 | emit station_updated(value); 18 | }); 19 | } 20 | 21 | QSize Tuner::sizeHint() const 22 | { 23 | auto size = QSlider::sizeHint(); 24 | 25 | return QSize(size.width(), size.height() + 96); 26 | } 27 | 28 | void Tuner::paintEvent(QPaintEvent *event) 29 | { 30 | QPainter painter(this); 31 | 32 | QFontMetrics fontMetrics = QFontMetrics(this->font()); 33 | 34 | double count = maximum() - minimum(); 35 | double tick_dist = (this->width() - 40) / count; 36 | for (int i = 0; i <= count; i++) { 37 | double x = (i * tick_dist) + 20; 38 | double y = this->height(); 39 | 40 | int tick_num = i + minimum(); 41 | if (tick_num % 10 == 0) { 42 | painter.setPen(QPen(this->color, 2)); 43 | painter.drawLine(QLineF(x, 32, x, this->height() - 32)); 44 | 45 | tick_num /= 10; 46 | double font_width = fontMetrics.width(QString::number(tick_num)); 47 | painter.drawText(QPointF(x - (font_width / 2.0), y), QString::number(tick_num)); 48 | } 49 | else if (tick_num % 5 == 0) { 50 | painter.setPen(QPen(this->color, 2)); 51 | painter.drawLine(QLineF(x, 40, x, this->height() - 40)); 52 | } 53 | else { 54 | painter.setPen(QPen(this->color, 1)); 55 | painter.drawLine(QLineF(x, 48, x, this->height() - 48)); 56 | } 57 | } 58 | 59 | QSlider::paintEvent(event); 60 | } 61 | -------------------------------------------------------------------------------- /cmake_modules/Findrtaudio.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This file is part of openauto project. 3 | # Copyright (C) 2018 f1x.studio (Michal Szwaj) 4 | # 5 | # openauto is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | 10 | # openauto is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with openauto. If not, see . 17 | # 18 | 19 | if (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS) 20 | # in cache already 21 | set(RTAUDIO_FOUND TRUE) 22 | else (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS) 23 | find_path(RTAUDIO_INCLUDE_DIR 24 | NAMES 25 | RtAudio.h 26 | PATHS 27 | /usr/include 28 | /usr/local/include 29 | /opt/local/include 30 | /sw/include 31 | PATH_SUFFIXES 32 | rtaudio 33 | ) 34 | 35 | find_library(RTAUDIO_LIBRARY 36 | NAMES 37 | rtaudio 38 | PATHS 39 | /usr/lib 40 | /usr/local/lib 41 | /opt/local/lib 42 | /sw/lib 43 | ) 44 | 45 | set(RTAUDIO_INCLUDE_DIRS 46 | ${RTAUDIO_INCLUDE_DIR} 47 | ) 48 | set(RTAUDIO_LIBRARIES 49 | ${RTAUDIO_LIBRARY} 50 | ) 51 | 52 | if (RTAUDIO_INCLUDE_DIRS AND RTAUDIO_LIBRARIES) 53 | set(RTAUDIO_FOUND TRUE) 54 | endif (RTAUDIO_INCLUDE_DIRS AND RTAUDIO_LIBRARIES) 55 | 56 | if (RTAUDIO_FOUND) 57 | if (NOT rtaudio_FIND_QUIETLY) 58 | message(STATUS "Found rtaudio:") 59 | message(STATUS " - Includes: ${RTAUDIO_INCLUDE_DIRS}") 60 | message(STATUS " - Libraries: ${RTAUDIO_LIBRARIES}") 61 | endif (NOT rtaudio_FIND_QUIETLY) 62 | else (RTAUDIO_FOUND) 63 | if (rtaudio_FIND_REQUIRED) 64 | message(FATAL_ERROR "Could not find rtaudio") 65 | endif (rtaudio_FIND_REQUIRED) 66 | endif (RTAUDIO_FOUND) 67 | 68 | mark_as_advanced(RTAUDIO_INCLUDE_DIRS RTAUDIO_LIBRARIES) 69 | 70 | endif (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS) 71 | -------------------------------------------------------------------------------- /include/app/config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_HPP_ 2 | #define CONFIG_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class Config : public QObject { 11 | Q_OBJECT 12 | 13 | public: 14 | Config(); 15 | 16 | void save(); 17 | 18 | inline int get_volume() { return this->volume; } 19 | inline void set_volume(int volume) { this->volume = volume; } 20 | 21 | inline bool get_dark_mode() { return this->dark_mode; } 22 | inline void set_dark_mode(bool dark_mode) { this->dark_mode = dark_mode; } 23 | 24 | inline int get_brightness() { return this->brightness; } 25 | inline void set_brightness(int brightness) 26 | { 27 | this->brightness = brightness; 28 | emit brightness_changed(this->brightness); 29 | } 30 | 31 | inline bool get_si_units() { return this->si_units; } 32 | inline void set_si_units(bool si_units) 33 | { 34 | this->si_units = si_units; 35 | emit si_units_changed(this->si_units); 36 | } 37 | 38 | inline QString get_color() { return this->color; } 39 | inline void set_color(QString color) { this->color = color; } 40 | 41 | inline QString get_bluetooth_device() { return this->bluetooth_device; } 42 | inline void set_bluetooth_device(QString bluetooth_device) { this->bluetooth_device = bluetooth_device; } 43 | 44 | inline double get_radio_station() { return this->radio_station; } 45 | inline void set_radio_station(double radio_station) { this->radio_station = radio_station; } 46 | 47 | inline bool get_radio_muted() { return this->radio_muted; } 48 | inline void set_radio_muted(bool radio_muted) { this->radio_muted = radio_muted; } 49 | 50 | std::shared_ptr open_auto_config; 51 | 52 | static Config *get_instance(); 53 | 54 | private: 55 | QSettings ia_config; 56 | int volume; 57 | bool dark_mode; 58 | int brightness; 59 | bool si_units; 60 | QString color; 61 | QString bluetooth_device; 62 | double radio_station; 63 | bool radio_muted; 64 | 65 | signals: 66 | void brightness_changed(unsigned int); 67 | void si_units_changed(bool); 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /include/app/tabs/open_auto.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OPEN_AUTO_HPP_ 2 | #define OPEN_AUTO_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace aasdk = f1x::aasdk; 19 | namespace autoapp = f1x::openauto::autoapp; 20 | 21 | class OpenAutoWorker { 22 | public: 23 | OpenAutoWorker(std::function callback = nullptr, QWidget *parent = nullptr); 24 | ~OpenAutoWorker(); 25 | 26 | inline void start() { this->app->waitForUSBDevice(); } 27 | inline void set_opacity(unsigned int alpha) { this->service_factory.setOpacity(alpha); } 28 | 29 | private: 30 | void create_usb_workers(); 31 | void create_io_service_workers(); 32 | 33 | libusb_context *usb_context; 34 | boost::asio::io_service io_service; 35 | boost::asio::io_service::work work; 36 | std::shared_ptr configuration; 37 | aasdk::tcp::TCPWrapper tcp_wrapper; 38 | aasdk::usb::USBWrapper usb_wrapper; 39 | aasdk::usb::AccessoryModeQueryFactory query_factory; 40 | aasdk::usb::AccessoryModeQueryChainFactory query_chain_factory; 41 | autoapp::service::ServiceFactory service_factory; 42 | autoapp::service::AndroidAutoEntityFactory android_auto_entity_factory; 43 | std::shared_ptr usb_hub; 44 | std::shared_ptr connected_accessories_enumerator; 45 | std::shared_ptr app; 46 | std::vector thread_pool; 47 | }; 48 | 49 | class OpenAutoTab : public QWidget { 50 | Q_OBJECT 51 | 52 | public: 53 | OpenAutoTab(QWidget *parent = nullptr); 54 | 55 | void start_worker(); 56 | 57 | private: 58 | QWidget *msg_widget(); 59 | 60 | OpenAutoWorker *worker = nullptr; 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/app/config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | Config::Config() 7 | : QObject(qApp), 8 | open_auto_config(std::make_shared()), 9 | ia_config(QSettings::IniFormat, QSettings::UserScope, "ia") 10 | { 11 | this->volume = this->ia_config.value("volume", 50).toInt(); 12 | this->dark_mode = this->ia_config.value("dark_mode", false).toBool(); 13 | this->brightness = this->ia_config.value("brightness", 255).toInt(); 14 | this->si_units = this->ia_config.value("si_units", false).toBool(); 15 | this->color = this->ia_config.value("color", "blue").toString(); 16 | this->bluetooth_device = this->ia_config.value("Bluetooth/device", QString()).toString(); 17 | this->radio_station = this->ia_config.value("Radio/station", 98.0).toDouble(); 18 | this->radio_muted = this->ia_config.value("Radio/muted", true).toBool(); 19 | 20 | QTimer *timer = new QTimer(this); 21 | connect(timer, &QTimer::timeout, [this]() { this->save(); }); 22 | timer->start(10000); 23 | } 24 | 25 | void Config::save() 26 | { 27 | if (this->volume != this->ia_config.value("volume", 50).toInt()) 28 | this->ia_config.setValue("volume", this->volume); 29 | if (this->dark_mode != this->ia_config.value("dark_mode", false).toBool()) 30 | this->ia_config.setValue("dark_mode", this->dark_mode); 31 | if (this->brightness != this->ia_config.value("brightness", 255).toInt()) 32 | this->ia_config.setValue("brightness", this->brightness); 33 | if (this->si_units != this->ia_config.value("si_units", false).toBool()) 34 | this->ia_config.setValue("si_units", this->si_units); 35 | if (this->color != this->ia_config.value("color", "blue").toString()) 36 | this->ia_config.setValue("color", this->color); 37 | if (this->bluetooth_device != this->ia_config.value("Bluetooth/device", QString()).toString()) 38 | this->ia_config.setValue("Bluetooth/device", this->bluetooth_device); 39 | if (this->radio_station != this->ia_config.value("Radio/station", 98.0).toDouble()) 40 | this->ia_config.setValue("Radio/station", this->radio_station); 41 | if (this->radio_muted != this->ia_config.value("Radio/muted", true).toBool()) 42 | this->ia_config.setValue("Radio/muted", this->radio_muted); 43 | 44 | this->open_auto_config->save(); 45 | } 46 | 47 | Config *Config::get_instance() 48 | { 49 | static Config config; 50 | return &config; 51 | } 52 | -------------------------------------------------------------------------------- /src/app/widgets/switch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | Switch::Switch(QWidget* parent) : QAbstractButton(parent) 6 | { 7 | QAbstractButton::setCheckable(true); 8 | QAbstractButton::setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 9 | 10 | this->track_radius = 8; 11 | this->thumb_radius = 12; 12 | this->margin = std::max(0, this->thumb_radius - this->track_radius); 13 | this->base_offset = std::max(this->thumb_radius, this->track_radius); 14 | this->end_offset = [this](bool checked) { return checked ? this->width() - this->base_offset : this->base_offset; }; 15 | this->offset = this->base_offset; 16 | } 17 | 18 | QSize Switch::sizeHint() const 19 | { 20 | return QSize(4.5 * this->track_radius + 2 * this->margin, 2 * this->track_radius + 2 * this->margin); 21 | } 22 | 23 | void Switch::paintEvent(QPaintEvent*) 24 | { 25 | QPainter painter(this); 26 | 27 | painter.setRenderHint(QPainter::Antialiasing); 28 | painter.setPen(Qt::NoPen); 29 | 30 | QColor track_brush = this->isChecked() ? palette().color(QPalette::AlternateBase) : this->track_color; 31 | QColor thumb_brush = this->isChecked() ? palette().color(QPalette::Base) : this->thumb_color; 32 | 33 | painter.setBrush(track_brush); 34 | painter.drawRoundedRect(this->margin, this->margin, this->width() - 2 * this->margin, 35 | this->height() - 2 * this->margin, this->track_radius, this->track_radius); 36 | painter.setBrush(thumb_brush); 37 | painter.drawEllipse(this->offset - this->thumb_radius, this->base_offset - this->thumb_radius, 38 | 2 * this->thumb_radius, 2 * this->thumb_radius); 39 | } 40 | 41 | void Switch::resizeEvent(QResizeEvent* event) 42 | { 43 | QAbstractButton::resizeEvent(event); 44 | 45 | this->offset = this->end_offset(this->isChecked()); 46 | } 47 | 48 | void Switch::mouseReleaseEvent(QMouseEvent* event) 49 | { 50 | QAbstractButton::mouseReleaseEvent(event); 51 | 52 | if (event->button() == Qt::LeftButton) { 53 | QPropertyAnimation *offset_animation = new QPropertyAnimation(this, "offset"); 54 | offset_animation->setDuration(120); 55 | offset_animation->setStartValue(this->offset); 56 | offset_animation->setEndValue(this->end_offset(this->isChecked())); 57 | offset_animation->start(); 58 | } 59 | } 60 | 61 | void Switch::nextCheckState() 62 | { 63 | QAbstractButton::nextCheckState(); 64 | 65 | bool checked = this->isChecked(); 66 | this->offset = this->end_offset(checked); 67 | emit stateChanged(checked); 68 | } 69 | -------------------------------------------------------------------------------- /assets/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/dark/play-24px.svg 4 | icons/dark/pause-24px.svg 5 | icons/dark/brightness_low-24px.svg 6 | icons/dark/play_circle_outline-24px.svg 7 | icons/dark/speed-24px.svg 8 | icons/dark/brightness_high-24px.svg 9 | icons/dark/report_problem-24px.svg 10 | icons/dark/tune-24px.svg 11 | icons/dark/skip_next-24px.svg 12 | icons/dark/skip_previous-24px.svg 13 | icons/dark/directions_car-24px.svg 14 | icons/dark/volume_mute-24px.svg 15 | icons/dark/volume_up-24px.svg 16 | icons/dark/arrow_drop_down-24px.svg 17 | icons/dark/check-24px.svg 18 | icons/dark/fast_forward-24px.svg 19 | icons/dark/fast_rewind-24px.svg 20 | icons/dark/volume_off-24px.svg 21 | icons/dark/save-24px.svg 22 | icons/dark/bluetooth_searching-24px.svg 23 | icons/light/play-24px.svg 24 | icons/light/pause-24px.svg 25 | icons/light/brightness_low-24px.svg 26 | icons/light/play_circle_outline-24px.svg 27 | icons/light/speed-24px.svg 28 | icons/light/brightness_high-24px.svg 29 | icons/light/report_problem-24px.svg 30 | icons/light/tune-24px.svg 31 | icons/light/skip_next-24px.svg 32 | icons/light/skip_previous-24px.svg 33 | icons/light/directions_car-24px.svg 34 | icons/light/volume_mute-24px.svg 35 | icons/light/volume_up-24px.svg 36 | icons/light/arrow_drop_down-24px.svg 37 | icons/light/check-24px.svg 38 | icons/light/fast_forward-24px.svg 39 | icons/light/fast_rewind-24px.svg 40 | icons/light/volume_off-24px.svg 41 | icons/light/save-24px.svg 42 | icons/light/bluetooth_searching-24px.svg 43 | stylesheets/light.qss 44 | stylesheets/dark.qss 45 | fonts/Titillium_Web/TitilliumWeb-Regular.ttf 46 | fonts/Montserrat/Montserrat-LightItalic.ttf 47 | fonts/Montserrat/Montserrat-Regular.ttf 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/app/widgets/progress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | ProgressIndicator::ProgressIndicator(QWidget* parent) : QWidget(parent) 10 | { 11 | setFocusPolicy(Qt::NoFocus); 12 | 13 | QParallelAnimationGroup* group = new QParallelAnimationGroup(this); 14 | group->setLoopCount(-1); 15 | 16 | this->dash_length=0; 17 | QPropertyAnimation *length_animation = new QPropertyAnimation(this, "dash_length"); 18 | length_animation->setEasingCurve(QEasingCurve::InOutQuad); 19 | length_animation->setStartValue(0.1); 20 | length_animation->setKeyValueAt(0.15, 1); 21 | length_animation->setKeyValueAt(0.5, 18); 22 | length_animation->setKeyValueAt(0.6, 18); 23 | length_animation->setEndValue(18); 24 | length_animation->setDuration(1500); 25 | group->addAnimation(length_animation); 26 | 27 | QPropertyAnimation *offset_animation = new QPropertyAnimation(this, "dash_offset"); 28 | offset_animation->setEasingCurve(QEasingCurve::InOutSine); 29 | offset_animation->setStartValue(0); 30 | offset_animation->setKeyValueAt(0.15, 0); 31 | offset_animation->setKeyValueAt(0.7, -7); 32 | offset_animation->setKeyValueAt(0.75, -7); 33 | offset_animation->setEndValue(-25); 34 | offset_animation->setDuration(1500); 35 | group->addAnimation(offset_animation); 36 | 37 | QPropertyAnimation *angle_animation = new QPropertyAnimation(this, "angle"); 38 | angle_animation->setStartValue(-90); 39 | angle_animation->setEndValue(270); 40 | angle_animation->setDuration(1500); 41 | group->addAnimation(angle_animation); 42 | 43 | group->start(); 44 | } 45 | 46 | void ProgressIndicator::startAnimation() 47 | { 48 | if (this->timer_id == -1) this->timer_id = startTimer(0); 49 | this->enabled = true; 50 | } 51 | 52 | void ProgressIndicator::stopAnimation() 53 | { 54 | if (this->timer_id != -1) killTimer(this->timer_id); 55 | this->enabled = false; 56 | 57 | this->timer_id = -1; 58 | 59 | update(); 60 | } 61 | 62 | void ProgressIndicator::paintEvent(QPaintEvent*) 63 | { 64 | QPainter painter(this); 65 | painter.setRenderHint(QPainter::Antialiasing); 66 | 67 | if (!enabled) return; 68 | 69 | painter.translate(width() / 2, height() / 2); 70 | painter.rotate(this->angle); 71 | 72 | QPen pen; 73 | pen.setWidth(3); 74 | pen.setColor(palette().color(QPalette::Base)); 75 | 76 | QVector pattern; 77 | pattern << this->dash_length * 48 / 36 << 30 * 48 / 36; 78 | 79 | pen.setDashOffset(this->dash_offset * 48 / 36); 80 | pen.setDashPattern(pattern); 81 | 82 | painter.setPen(pen); 83 | 84 | painter.drawEllipse(QPoint(0, 0), 48 / 3, 48 / 3); 85 | } 86 | -------------------------------------------------------------------------------- /src/app/bluetooth.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | Bluetooth::Bluetooth() : QObject(qApp) 22 | { 23 | BluezQt::Manager *manager = new BluezQt::Manager(); 24 | BluezQt::InitManagerJob *job = manager->init(); 25 | job->exec(); 26 | 27 | this->adapter = manager->usableAdapter(); 28 | 29 | if (this->has_adapter()) { 30 | for (auto device : this->get_devices()) { 31 | if (device->mediaPlayer() != nullptr) { 32 | this->media_player_device = device; 33 | break; 34 | } 35 | } 36 | 37 | connect(this->adapter.get(), &BluezQt::Adapter::deviceAdded, 38 | [this](BluezQt::DevicePtr device) { emit device_added(device); }); 39 | connect(this->adapter.get(), &BluezQt::Adapter::deviceChanged, [this](BluezQt::DevicePtr device) { 40 | emit device_changed(device); 41 | this->update_media_player(device); 42 | }); 43 | connect(this->adapter.get(), &BluezQt::Adapter::deviceRemoved, 44 | [this](BluezQt::DevicePtr device) { emit device_removed(device); }); 45 | } 46 | } 47 | 48 | void Bluetooth::scan() 49 | { 50 | if (this->has_adapter()) { 51 | emit scan_status(true); 52 | this->adapter->startDiscovery(); 53 | QTimer::singleShot(10000, [this]() { 54 | this->adapter->stopDiscovery(); 55 | emit scan_status(false); 56 | }); 57 | } 58 | } 59 | 60 | void Bluetooth::update_media_player(BluezQt::DevicePtr device) 61 | { 62 | if (device->mediaPlayer() != nullptr) { 63 | emit media_player_status_changed(device->mediaPlayer()->status()); 64 | emit media_player_track_changed(device->mediaPlayer()->track()); 65 | emit media_player_changed(device->name(), device->mediaPlayer()); 66 | this->media_player_device = device; 67 | } 68 | else if (this->media_player_device == device) { 69 | emit media_player_status_changed(BluezQt::MediaPlayer::Status::Paused); 70 | emit media_player_track_changed(BluezQt::MediaPlayerTrack()); 71 | emit media_player_changed(QString(), nullptr); 72 | } 73 | } 74 | 75 | Bluetooth *Bluetooth::get_instance() 76 | { 77 | static Bluetooth bluetooth; 78 | return &bluetooth; 79 | } 80 | -------------------------------------------------------------------------------- /include/app/theme.hpp: -------------------------------------------------------------------------------- 1 | #ifndef THEME_HPP_ 2 | #define THEME_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef QPair tab_icon_t; 12 | typedef QPair button_icon_t; 13 | 14 | class Theme : public QObject { 15 | Q_OBJECT 16 | 17 | public: 18 | enum Orientation { BOTTOM, RIGHT }; 19 | 20 | static const QFont font_14; 21 | static const QFont font_16; 22 | static const QFont font_18; 23 | static const QFont font_36; 24 | 25 | static const QSize icon_16; 26 | static const QSize icon_24; 27 | static const QSize icon_32; 28 | static const QSize icon_36; 29 | static const QSize icon_42; 30 | static const QSize icon_48; 31 | static const QSize icon_56; 32 | static const QSize icon_84; 33 | static const QSize icon_96; 34 | 35 | static const QColor danger_color; 36 | static const QColor success_color; 37 | 38 | Theme(); 39 | 40 | inline void set_mode(bool mode) 41 | { 42 | this->mode = mode; 43 | this->update(); 44 | } 45 | inline void set_color(QString color) 46 | { 47 | this->color = color; 48 | this->update(); 49 | } 50 | inline const QMap get_colors() { return this->colors[this->mode ? "dark" : "light"]; } 51 | 52 | void add_tab_icon(QString name, int index, Qt::Orientation orientation = Qt::Orientation::Horizontal); 53 | void add_button_icon(QString name, QPushButton *button, QString active_name = QString()); 54 | void update(); 55 | 56 | inline static QFrame *br(QWidget *parent = nullptr, bool vertical = false) 57 | { 58 | QFrame *br = new QFrame(parent); 59 | br->setLineWidth(1); 60 | br->setFrameShape(vertical ? QFrame::VLine : QFrame::HLine); 61 | br->setFrameShadow(QFrame::Plain); 62 | 63 | return br; 64 | } 65 | static Theme *get_instance(); 66 | 67 | private: 68 | QMap> colors = {{"light", 69 | {{"blue", QColor(33, 150, 243)}, 70 | {"red", QColor(244, 67, 54)}, 71 | {"green", QColor(76, 175, 80)}, 72 | {"orange", QColor(255, 152, 0)}, 73 | {"steel", QColor(96, 125, 139)}}}, 74 | {"dark", 75 | {{"blue", QColor(144, 202, 249)}, 76 | {"red", QColor(239, 154, 154)}, 77 | {"green", QColor(165, 214, 167)}, 78 | {"orange", QColor(255, 204, 128)}, 79 | {"steel", QColor(176, 190, 197)}}}}; 80 | 81 | QPalette palette; 82 | QString color; 83 | QMap> tab_icons; 84 | QMap> button_icons; 85 | QMap stylesheets; 86 | bool mode = false; 87 | 88 | void set_palette(); 89 | QString parse_stylesheet(QString file); 90 | QPixmap create_pixmap_variant(QPixmap &base, qreal opacity); 91 | 92 | signals: 93 | void icons_updated(QList &, QList &); 94 | void color_updated(QMap &); 95 | }; 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project(ia CXX) 3 | 4 | add_subdirectory(aasdk) 5 | set(AASDK_INCLUDE_DIRS ${aasdk_SOURCE_DIR}/include) 6 | set(AASDK_LIBRARIES ${aasdk_SOURCE_DIR}/lib/libaasdk.so) 7 | set(AASDK_PROTO_INCLUDE_DIRS ${aasdk_BINARY_DIR}) 8 | set(AASDK_PROTO_LIBRARIES ${aasdk_SOURCE_DIR}/lib/libaasdk_proto.so) 9 | 10 | add_subdirectory(openauto) 11 | set(OPENAUTO_INCLUDE_DIRS ${openauto_SOURCE_DIR}/include) 12 | 13 | set(CMAKE_AUTOMOC ON) 14 | set(CMAKE_AUTOUIC ON) 15 | set(CMAKE_AUTORCC ON) 16 | set(base_directory ${CMAKE_CURRENT_SOURCE_DIR}) 17 | set(resources_directory ${base_directory}/assets) 18 | set(sources_directory ${base_directory}/src) 19 | set(include_directory ${base_directory}/include) 20 | 21 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${base_directory}/bin) 22 | set(EXECUTABLE_OUTPUT_PATH ${base_directory}/bin) 23 | 24 | set(Boost_USE_STATIC_LIBS OFF) 25 | set(Boost_USE_MULTITHREADED ON) 26 | set(Boost_USE_STATIC_RUNTIME OFF) 27 | 28 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") 29 | SET(CMAKE_CXX_STANDARD 14) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT} -Wall -pedantic -fPIC") 31 | set(CMAKE_CXX_FLAGS_DEBUG "-g -O0") 32 | set(CMAKE_CXX_FLAGS_RELEASE "-g -O3") 33 | 34 | add_definitions(-DBOOST_ALL_DYN_LINK) 35 | 36 | find_package(Boost REQUIRED COMPONENTS system log OPTIONAL_COMPONENTS unit_test_framework) 37 | find_package(libusb-1.0 REQUIRED) 38 | find_package(Qt5 COMPONENTS Multimedia MultimediaWidgets Bluetooth) 39 | find_package(KF5BluezQt REQUIRED) 40 | find_package(Protobuf REQUIRED) 41 | find_package(OpenSSL REQUIRED) 42 | find_package(rtaudio REQUIRED) 43 | 44 | if(RPI_BUILD) 45 | add_definitions(-DUSE_OMX -DOMX_SKIP64BIT) 46 | set(BCM_HOST_LIBRARIES "/opt/vc/lib/libbcm_host.so") 47 | set(BCM_HOST_INCLUDE_DIRS "/opt/vc/include") 48 | set(ILCLIENT_INCLUDE_DIRS "/opt/vc/src/hello_pi/libs/ilclient") 49 | set(ILCLIENT_LIBRARIES "/opt/vc/src/hello_pi/libs/ilclient/libilclient.a;/opt/vc/lib/libvcos.so;/opt/vc/lib/libvcilcs.a;/opt/vc/lib/libvchiq_arm.so") 50 | endif(RPI_BUILD) 51 | 52 | include_directories(${CMAKE_CURRENT_BINARY_DIR} 53 | ${Qt5Multimedia_INCLUDE_DIRS} 54 | ${Qt5MultimediaWidgets_INCLUDE_DIRS} 55 | ${Qt5Widgets_INCLUDE_DIRS} 56 | ${Qt5Bluetooth_INCLUDE_DIRS} 57 | ${Boost_INCLUDE_DIRS} 58 | ${LIBUSB_1_INCLUDE_DIRS} 59 | ${PROTOBUF_INCLUDE_DIR} 60 | ${OPENSSL_INCLUDE_DIR} 61 | ${RTAUDIO_INCLUDE_DIRS} 62 | ${AASDK_PROTO_INCLUDE_DIRS} 63 | ${AASDK_INCLUDE_DIRS} 64 | ${OPENAUTO_INCLUDE_DIRS} 65 | ${BCM_HOST_INCLUDE_DIRS} 66 | ${ILCLIENT_INCLUDE_DIRS} 67 | ${include_directory}) 68 | 69 | link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 70 | 71 | file(GLOB_RECURSE source_files ${sources_directory}/*.cpp ${include_directory}/*.hpp ${resources_directory}/*.qrc) 72 | 73 | add_executable(ia ${source_files}) 74 | 75 | add_dependencies(auto aasdk) 76 | target_link_libraries(ia 77 | aasdk_proto 78 | aasdk 79 | auto 80 | ${Boost_LIBRARIES} 81 | ${Qt5Multimedia_LIBRARIES} 82 | ${Qt5MultimediaWidgets_LIBRARIES} 83 | ${Qt5Bluetooth_LIBRARIES} 84 | KF5::BluezQt 85 | ${LIBUSB_1_LIBRARIES} 86 | ${PROTOBUF_LIBRARIES} 87 | ${BCM_HOST_LIBRARIES} 88 | ${ILCLIENT_LIBRARIES} 89 | ${WINSOCK2_LIBRARIES} 90 | ${RTAUDIO_LIBRARIES}) 91 | 92 | -------------------------------------------------------------------------------- /cmake_modules/Findlibusb-1.0.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libusb-1.0 2 | # Once done this will define 3 | # 4 | # LIBUSB_1_FOUND - system has libusb 5 | # LIBUSB_1_INCLUDE_DIRS - the libusb include directory 6 | # LIBUSB_1_LIBRARIES - Link these to use libusb 7 | # LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb 8 | # 9 | # Adapted from cmake-modules Google Code project 10 | # 11 | # Copyright (c) 2006 Andreas Schneider 12 | # 13 | # (Changes for libusb) Copyright (c) 2008 Kyle Machulis 14 | # 15 | # Redistribution and use is allowed according to the terms of the New BSD license. 16 | # 17 | # CMake-Modules Project New BSD License 18 | # 19 | # Redistribution and use in source and binary forms, with or without 20 | # modification, are permitted provided that the following conditions are met: 21 | # 22 | # * Redistributions of source code must retain the above copyright notice, this 23 | # list of conditions and the following disclaimer. 24 | # 25 | # * Redistributions in binary form must reproduce the above copyright notice, 26 | # this list of conditions and the following disclaimer in the 27 | # documentation and/or other materials provided with the distribution. 28 | # 29 | # * Neither the name of the CMake-Modules Project nor the names of its 30 | # contributors may be used to endorse or promote products derived from this 31 | # software without specific prior written permission. 32 | # 33 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 34 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 35 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 36 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 37 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 38 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 39 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 40 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 42 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | # 44 | 45 | 46 | if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 47 | # in cache already 48 | set(LIBUSB_FOUND TRUE) 49 | else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 50 | find_path(LIBUSB_1_INCLUDE_DIR 51 | NAMES 52 | libusb.h 53 | PATHS 54 | /usr/include 55 | /usr/local/include 56 | /opt/local/include 57 | /sw/include 58 | PATH_SUFFIXES 59 | libusb-1.0 60 | ) 61 | 62 | find_library(LIBUSB_1_LIBRARY 63 | NAMES 64 | usb-1.0 usb 65 | PATHS 66 | /usr/lib 67 | /usr/local/lib 68 | /opt/local/lib 69 | /sw/lib 70 | ) 71 | 72 | set(LIBUSB_1_INCLUDE_DIRS 73 | ${LIBUSB_1_INCLUDE_DIR} 74 | ) 75 | set(LIBUSB_1_LIBRARIES 76 | ${LIBUSB_1_LIBRARY} 77 | ) 78 | 79 | if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) 80 | set(LIBUSB_1_FOUND TRUE) 81 | endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) 82 | 83 | if (LIBUSB_1_FOUND) 84 | if (NOT libusb_1_FIND_QUIETLY) 85 | message(STATUS "Found libusb-1.0:") 86 | message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}") 87 | message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}") 88 | endif (NOT libusb_1_FIND_QUIETLY) 89 | else (LIBUSB_1_FOUND) 90 | if (libusb_1_FIND_REQUIRED) 91 | message(FATAL_ERROR "Could not find libusb") 92 | endif (libusb_1_FIND_REQUIRED) 93 | endif (LIBUSB_1_FOUND) 94 | 95 | # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view 96 | mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES) 97 | 98 | endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 99 | -------------------------------------------------------------------------------- /assets/stylesheets/light.qss: -------------------------------------------------------------------------------- 1 | QMainWindow { 2 | background-color: rgb(238, 238, 238); 3 | } 4 | 5 | QLabel { 6 | color: rgba(0, 0, 0, 222); 7 | } 8 | 9 | QTabWidget::pane { 10 | background-color: rgba(255, 255, 255, 80); 11 | border-top: 0px; 12 | } 13 | 14 | QTabWidget::tab-bar[tabPosition="2"] { 15 | alignment: center; 16 | } 17 | 18 | QTabBar::tab { 19 | background-color: transparent; 20 | color: rgba(0, 0, 0, 138); 21 | padding: 4px; 22 | } 23 | 24 | QTabBar::tab:selected:top { 25 | color: palette(base); 26 | border-bottom: 2px solid palette(base); 27 | } 28 | 29 | QTabBar::tab:disabled:top { 30 | color: rgba(0, 0, 0, 97); 31 | } 32 | 33 | QSlider { 34 | height: 30px; 35 | } 36 | 37 | QSlider::groove { 38 | background-color: palette(alternate-base); 39 | height: 4px; 40 | } 41 | 42 | QSlider::handle { 43 | background-color: palette(base); 44 | width: 24px; 45 | height: 4px; 46 | line-height: 25px; 47 | margin-top: -10px; 48 | margin-bottom: -10px; 49 | border-radius: 12px; 50 | } 51 | 52 | QSlider::sub-page { 53 | background-color: palette(base); 54 | height: 4px; 55 | } 56 | 57 | QPushButton { 58 | color: rgba(0, 0, 0, 222); 59 | border: none; 60 | padding: 8px; 61 | } 62 | 63 | QPushButton:pressed { 64 | background-color: rgba(0, 0, 0, 25); 65 | } 66 | 67 | QPushButton:disabled { 68 | color: rgba(0, 0, 0, 97); 69 | } 70 | 71 | QPushButton:!flat { 72 | background-color: rgba(0, 0, 0, 31); 73 | } 74 | 75 | QPushButton:!flat:checked { 76 | color: rgba(255, 255, 255, 222); 77 | background-color: palette(base); 78 | } 79 | 80 | QPushButton:!flat:disabled { 81 | background-color: rgba(0, 0, 0, 13); 82 | } 83 | 84 | QFrame[frameShape="4"][frameShadow="16"], 85 | QFrame[frameShape="5"][frameShadow="16"] { 86 | color: rgba(0, 0, 0, 31); 87 | } 88 | 89 | QComboBox { 90 | color: rgba(0, 0, 0, 222); 91 | background: transparent; 92 | border: none; 93 | border-bottom: 2px solid rgba(0, 0, 0, 31); 94 | } 95 | 96 | QComboBox::drop-down { 97 | border: none; 98 | padding-right: 16px; 99 | } 100 | 101 | QComboBox::down-arrow { 102 | image: url(:/icons/light/arrow_drop_down-24px.svg); 103 | } 104 | 105 | QComboBox QAbstractItemView::item { 106 | border: none; 107 | color: rgba(0, 0, 0, 222); 108 | background-color: rgb(245, 245, 245); 109 | } 110 | 111 | QGroupBox { 112 | border: none; 113 | } 114 | 115 | QCheckBox { 116 | color: rgba(0, 0, 0, 222); 117 | background-color: transparent; 118 | } 119 | 120 | QRadioButton { 121 | color: rgba(0, 0, 0, 222); 122 | background-color: transparent; 123 | } 124 | 125 | QCheckBox::indicator { 126 | width: 22px; 127 | height: 22px; 128 | border: 2px solid rgb(97, 97, 97); 129 | } 130 | 131 | QRadioButton::indicator { 132 | width: 18px; 133 | height: 18px; 134 | border: 2px solid rgb(97, 97, 97); 135 | border-radius: 11px; 136 | } 137 | 138 | QCheckBox::indicator::checked { 139 | image: url(:/icons/light/check-24px.svg); 140 | background-color: palette(base); 141 | border: 2px solid palette(base); 142 | } 143 | 144 | QRadioButton::indicator::checked { 145 | background-color: qradialgradient(cx:0.5, cy:0.5, fx:0.5, fy:0.5, radius:.4, stop:.65 palette(base), stop:.75 transparent); 146 | border: 2px solid palette(base); 147 | } 148 | 149 | Switch { 150 | qproperty-track_color: rgba(0, 0, 0, 64); 151 | qproperty-thumb_color: rgb(224, 224, 224); 152 | } 153 | 154 | Tuner { 155 | qproperty-color: rgb(0, 0, 0); 156 | padding-left: 16px; 157 | padding-right: 16px; 158 | } 159 | 160 | Tuner::groove { 161 | background-color: rgb(0, 0, 0); 162 | } 163 | 164 | Tuner::handle { 165 | width: 6px; 166 | margin-top: -32px; 167 | margin-bottom: -32px; 168 | } 169 | 170 | Tuner::sub-page { 171 | background-color: rgb(0, 0, 0); 172 | } 173 | -------------------------------------------------------------------------------- /assets/stylesheets/dark.qss: -------------------------------------------------------------------------------- 1 | QMainWindow { 2 | background-color: rgb(18, 18, 18); 3 | } 4 | 5 | QLabel { 6 | color: rgb(255, 255, 255); 7 | } 8 | 9 | QTabWidget::pane { 10 | background-color: rgba(255, 255, 255, 5); 11 | border-top: 0px; 12 | } 13 | 14 | QTabWidget::tab-bar[tabPosition="2"] { 15 | alignment: center; 16 | } 17 | 18 | QTabBar::tab { 19 | background-color: transparent; 20 | color: rgba(255, 255, 255, 179); 21 | padding: 4px; 22 | } 23 | 24 | QTabBar::tab:selected:top { 25 | color: palette(base); 26 | border-bottom: 2px solid palette(base); 27 | } 28 | 29 | QTabBar::tab:disabled:top { 30 | color: rgba(255, 255, 255, 128); 31 | } 32 | 33 | QSlider { 34 | height: 30px; 35 | } 36 | 37 | QSlider::groove { 38 | background-color: palette(alternate-base); 39 | height: 4px; 40 | } 41 | 42 | QSlider::handle { 43 | background-color: palette(base); 44 | width: 24px; 45 | height: 4px; 46 | line-height: 25px; 47 | margin-top: -10px; 48 | margin-bottom: -10px; 49 | border-radius: 12px; 50 | } 51 | 52 | QSlider::sub-page { 53 | background-color: palette(base); 54 | height: 4px; 55 | } 56 | 57 | QPushButton { 58 | color: rgb(255, 255, 255); 59 | border: none; 60 | padding: 8px; 61 | } 62 | 63 | QPushButton:pressed { 64 | background-color: rgba(255, 255, 255, 25); 65 | } 66 | 67 | QPushButton:disabled { 68 | color: rgba(255, 255, 255, 128); 69 | } 70 | 71 | QPushButton:!flat { 72 | background-color: rgba(255, 255, 255, 31); 73 | } 74 | 75 | QPushButton:!flat:checked { 76 | color: rgb(0, 0, 0); 77 | background-color: palette(base); 78 | } 79 | 80 | QPushButton:!flat:disabled { 81 | background-color: rgba(255, 255, 255, 13); 82 | } 83 | 84 | QFrame[frameShape="4"][frameShadow="16"], 85 | QFrame[frameShape="5"][frameShadow="16"] { 86 | color: rgba(255, 255, 255, 31); 87 | } 88 | 89 | QComboBox { 90 | color: rgb(255, 255, 255); 91 | background: transparent; 92 | border: none; 93 | border-bottom: 2px solid rgba(255, 255, 255, 31); 94 | } 95 | 96 | QComboBox::drop-down { 97 | border: none; 98 | padding-right: 16px; 99 | } 100 | 101 | QComboBox::down-arrow { 102 | image: url(:/icons/dark/arrow_drop_down-24px.svg); 103 | } 104 | 105 | QComboBox QAbstractItemView::item { 106 | border: none; 107 | color: rgb(255, 255, 255); 108 | background-color: rgb(33, 33, 33); 109 | } 110 | 111 | QGroupBox { 112 | border: none; 113 | } 114 | 115 | QCheckBox { 116 | color: rgb(255, 255, 255); 117 | background-color: transparent; 118 | } 119 | 120 | QRadioButton { 121 | color: rgb(255, 255, 255); 122 | background-color: transparent; 123 | } 124 | 125 | QCheckBox::indicator { 126 | width: 22px; 127 | height: 22px; 128 | border: 2px solid rgb(255, 255, 255); 129 | } 130 | 131 | QRadioButton::indicator { 132 | width: 18px; 133 | height: 18px; 134 | border: 2px solid rgb(255, 255, 255); 135 | border-radius: 11px; 136 | } 137 | 138 | QCheckBox::indicator::checked { 139 | image: url(:/icons/dark/check-24px.svg); 140 | background-color: palette(base); 141 | border: 2px solid palette(base); 142 | } 143 | 144 | QRadioButton::indicator::checked { 145 | background-color: qradialgradient(cx:0.5, cy:0.5, fx:0.5, fy:0.5, radius:.4, stop:.65 palette(base), stop:.75 transparent); 146 | border: 2px solid palette(base); 147 | } 148 | 149 | Switch { 150 | qproperty-track_color: rgba(255, 255, 255, 31); 151 | qproperty-thumb_color: rgb(158, 158, 158); 152 | } 153 | 154 | Tuner { 155 | qproperty-color: rgb(224, 224, 224); 156 | padding-left: 16px; 157 | padding-right: 16px; 158 | } 159 | 160 | Tuner::groove { 161 | background-color: rgb(224, 224, 224); 162 | } 163 | 164 | Tuner::handle { 165 | width: 6px; 166 | margin-top: -32px; 167 | margin-bottom: -32px; 168 | } 169 | 170 | Tuner::sub-page { 171 | background-color: rgb(224, 224, 224); 172 | } 173 | -------------------------------------------------------------------------------- /src/obd/obd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | OBD::OBD() 11 | { 12 | this->connect("/dev/ttyUSB0", B115200); 13 | if (this->connected) this->initialize(); 14 | } 15 | 16 | OBD::~OBD() 17 | { 18 | this->raw_query("ATZ"); 19 | sleep(1); 20 | close(this->fd); 21 | this->connected = false; 22 | } 23 | 24 | bool OBD::query(Command &cmd, double &val) 25 | { 26 | if (!this->connected) return false; 27 | 28 | std::lock_guard guard(this->obd_mutex); 29 | if (this->send(cmd.request)) { 30 | Response resp = this->receive(); 31 | val = cmd.decoder(resp); 32 | return resp.success; 33 | } 34 | else { 35 | std::cout << "unable to send command" << std::endl; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | OBD *OBD::get_instance() 42 | { 43 | static OBD obd; 44 | return &obd; 45 | } 46 | 47 | void OBD::connect(std::string dev_path, speed_t baudrate) 48 | { 49 | if (this->connected) return; 50 | 51 | this->fd = open(dev_path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); 52 | if (this->fd == -1) { 53 | this->connected = false; 54 | return; 55 | } 56 | 57 | fcntl(this->fd, F_SETFL, 0); 58 | 59 | struct termios options; 60 | tcgetattr(this->fd, &options); 61 | 62 | options.c_cflag |= (CLOCAL | CREAD); 63 | options.c_lflag &= !(ICANON | ECHO | ECHOE | ISIG); 64 | options.c_oflag &= !(OPOST); 65 | options.c_cc[VMIN] = 0; 66 | options.c_cc[VTIME] = 100; 67 | 68 | tcflush(this->fd, TCIOFLUSH); 69 | tcsetattr(this->fd, TCSANOW, &options); 70 | 71 | cfsetispeed(&options, baudrate); 72 | cfsetospeed(&options, baudrate); 73 | tcsetattr(this->fd, TCSANOW, &options); 74 | tcflush(this->fd, TCIOFLUSH); 75 | 76 | this->connected = true; 77 | } 78 | 79 | void OBD::initialize() 80 | { 81 | std::vector cmds = {"ATD", "ATZ", "ATE0", "ATL0", "ATS0", "ATH0", "ATSP0", "0100"}; 82 | for (auto const cmd : cmds) { 83 | this->raw_query(cmd); 84 | usleep(500000); 85 | } 86 | } 87 | 88 | bool OBD::send(Request req) 89 | { 90 | std::string req_str = req.to_str(); 91 | 92 | return this->_write(req_str) >= 0; 93 | } 94 | 95 | int OBD::_write(std::string str) 96 | { 97 | str += '\r'; 98 | int size; 99 | if ((size = write(this->fd, str.c_str(), str.length())) < 0) { 100 | std::cout << "failed write" << std::endl; 101 | this->connected = false; 102 | return 0; 103 | } 104 | 105 | return size; 106 | } 107 | 108 | bool OBD::is_failed_response(std::string str) 109 | { 110 | std::vector failed_msgs = { 111 | "UNABLE TO CONNECT", "BUS INIT...ERROR", "NO DATA", "STOPPED", "ERROR", "?"}; 112 | 113 | for (auto const &msg : failed_msgs) 114 | if (str.find(msg) != std::string::npos) return true; 115 | 116 | return false; 117 | } 118 | 119 | Response OBD::receive() 120 | { 121 | std::string resp_str = this->_read(); 122 | 123 | if (is_failed_response(resp_str)) return Response(); 124 | 125 | std::string searching_phrase = "SEARCHING..."; 126 | std::string::size_type i = resp_str.find(searching_phrase); 127 | if (i != std::string::npos) resp_str.erase(i, searching_phrase.length()); 128 | resp_str.erase( 129 | std::remove_if(resp_str.begin(), resp_str.end(), [](auto const &c) -> bool { return !std::isalnum(c); }), 130 | resp_str.end()); 131 | 132 | return Response(resp_str); 133 | } 134 | 135 | std::string OBD::_read() 136 | { 137 | char buf[1]; 138 | std::string str; 139 | 140 | while (true) { 141 | if (read(this->fd, (void *)buf, 1) != 1) { 142 | std::cout << "failed read" << std::endl; 143 | this->connected = false; 144 | return ""; 145 | } 146 | if (buf[0] == '>') 147 | break; 148 | else 149 | str += buf[0]; 150 | } 151 | 152 | return str; 153 | } 154 | -------------------------------------------------------------------------------- /src/app/tabs/open_auto.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | OpenAutoWorker::OpenAutoWorker(std::function callback, QWidget *parent) 7 | : io_service(), 8 | work(io_service), 9 | configuration(Config::get_instance()->open_auto_config), 10 | tcp_wrapper(), 11 | usb_wrapper((libusb_init(&this->usb_context), usb_context)), 12 | query_factory(usb_wrapper, io_service), 13 | query_chain_factory(usb_wrapper, io_service, query_factory), 14 | service_factory(io_service, configuration, parent, callback), 15 | android_auto_entity_factory(io_service, configuration, service_factory), 16 | usb_hub(std::make_shared(this->usb_wrapper, this->io_service, this->query_chain_factory)), 17 | connected_accessories_enumerator(std::make_shared( 18 | this->usb_wrapper, this->io_service, this->query_chain_factory)), 19 | app(std::make_shared(this->io_service, this->usb_wrapper, this->tcp_wrapper, 20 | this->android_auto_entity_factory, usb_hub, connected_accessories_enumerator)) 21 | { 22 | this->create_usb_workers(); 23 | this->create_io_service_workers(); 24 | } 25 | 26 | OpenAutoWorker::~OpenAutoWorker() 27 | { 28 | std::for_each(this->thread_pool.begin(), this->thread_pool.end(), 29 | std::bind(&std::thread::join, std::placeholders::_1)); 30 | libusb_exit(this->usb_context); 31 | } 32 | 33 | void OpenAutoWorker::create_usb_workers() 34 | { 35 | auto worker = [this]() { 36 | timeval event_timeout = {180, 0}; 37 | while (!this->io_service.stopped()) 38 | libusb_handle_events_timeout_completed(this->usb_context, &event_timeout, nullptr); 39 | }; 40 | 41 | this->thread_pool.emplace_back(worker); 42 | this->thread_pool.emplace_back(worker); 43 | this->thread_pool.emplace_back(worker); 44 | this->thread_pool.emplace_back(worker); 45 | } 46 | 47 | void OpenAutoWorker::create_io_service_workers() 48 | { 49 | auto worker = [this]() { this->io_service.run(); }; 50 | 51 | this->thread_pool.emplace_back(worker); 52 | this->thread_pool.emplace_back(worker); 53 | this->thread_pool.emplace_back(worker); 54 | this->thread_pool.emplace_back(worker); 55 | } 56 | 57 | OpenAutoTab::OpenAutoTab(QWidget *parent) : QWidget(parent) 58 | { 59 | MainWindow *window = qobject_cast(parent); 60 | 61 | connect(window, &MainWindow::set_open_auto_state, [this](unsigned int alpha) { 62 | if (this->worker != nullptr) this->worker->set_opacity(alpha); 63 | if (alpha > 0) this->setFocus(); 64 | }); 65 | 66 | QStackedLayout *layout = new QStackedLayout(this); 67 | layout->setContentsMargins(0, 0, 0, 0); 68 | 69 | layout->addWidget(this->msg_widget()); 70 | #ifdef USE_OMX 71 | QWidget *omx_backdrop = new QWidget(this); 72 | omx_backdrop->setStyleSheet("background-color: black;"); 73 | layout->addWidget(omx_backdrop); 74 | #endif 75 | } 76 | 77 | void OpenAutoTab::start_worker() 78 | { 79 | QStackedLayout *layout = qobject_cast(this->layout()); 80 | auto callback = [layout](bool is_active) { layout->setCurrentIndex(is_active ? 1 : 0); }; 81 | if (this->worker == nullptr) this->worker = new OpenAutoWorker(callback, this); 82 | 83 | this->worker->start(); 84 | } 85 | 86 | QWidget *OpenAutoTab::msg_widget() 87 | { 88 | QWidget *widget = new QWidget(this); 89 | QVBoxLayout *layout = new QVBoxLayout(widget); 90 | layout->setContentsMargins(24, 24, 24, 24); 91 | 92 | QLabel *top_msg = new QLabel("waiting for device...", widget); 93 | top_msg->setFont(Theme::font_16); 94 | top_msg->setAlignment(Qt::AlignHCenter); 95 | layout->addStretch(); 96 | layout->addWidget(top_msg); 97 | 98 | QLabel *bottom_msg = new QLabel("plug in your device to start OpenAuto", widget); 99 | bottom_msg->setFont(Theme::font_16); 100 | bottom_msg->setAlignment(Qt::AlignHCenter); 101 | layout->addWidget(bottom_msg); 102 | layout->addStretch(); 103 | 104 | return widget; 105 | } 106 | -------------------------------------------------------------------------------- /assets/fonts/Montserrat/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/fonts/Titillium_Web/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students of MA course of Visual design. Some rights reserved. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /src/app/window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | MainWindow::MainWindow() 9 | { 10 | this->config = Config::get_instance(); 11 | this->setWindowOpacity(this->config->get_brightness() / 255.0); 12 | 13 | this->theme = Theme::get_instance(); 14 | this->theme->set_mode(this->config->get_dark_mode()); 15 | this->theme->set_color(this->config->get_color()); 16 | 17 | this->open_auto_tab = new OpenAutoTab(this); 18 | 19 | QWidget *widget = new QWidget(this); 20 | QVBoxLayout *layout = new QVBoxLayout(widget); 21 | 22 | layout->addWidget(this->tabs_widget()); 23 | layout->addWidget(this->controls_widget()); 24 | 25 | setCentralWidget(widget); 26 | } 27 | 28 | QTabWidget *MainWindow::tabs_widget() 29 | { 30 | QTabWidget *widget = new QTabWidget(this); 31 | widget->setTabPosition(QTabWidget::TabPosition::West); 32 | widget->tabBar()->setIconSize(Theme::icon_48); 33 | 34 | widget->addTab(this->open_auto_tab, QString()); 35 | this->theme->add_tab_icon("directions_car", 0, Qt::Orientation::Vertical); 36 | 37 | widget->addTab(new BluetoothPlayerSubTab(this), QString()); 38 | this->theme->add_tab_icon("play_circle_outline", 1, Qt::Orientation::Vertical); 39 | 40 | widget->addTab(new DataTab(this), QString()); 41 | this->theme->add_tab_icon("speed", 2, Qt::Orientation::Vertical); 42 | 43 | widget->addTab(new SettingsTab(this), ""); 44 | this->theme->add_tab_icon("tune", 3, Qt::Orientation::Vertical); 45 | 46 | connect(this->config, &Config::brightness_changed, [this, widget](int position) { 47 | this->setWindowOpacity(position / 255.0); 48 | if (widget->currentIndex() == 0) emit set_open_auto_state(position); 49 | }); 50 | connect(this->theme, &Theme::icons_updated, 51 | [widget](QList &tab_icons, QList &button_icons) { 52 | for (auto &icon : tab_icons) widget->tabBar()->setTabIcon(icon.first, icon.second); 53 | for (auto &icon : button_icons) icon.first->setIcon(icon.second); 54 | }); 55 | connect(widget, &QTabWidget::currentChanged, [this](int index) { 56 | emit set_open_auto_state((index == 0) ? (windowOpacity() * 255) : 0); 57 | emit set_data_state(index == 2); 58 | }); 59 | 60 | return widget; 61 | } 62 | 63 | QWidget *MainWindow::controls_widget() 64 | { 65 | QWidget *widget = new QWidget(this); 66 | QHBoxLayout *layout = new QHBoxLayout(widget); 67 | layout->setContentsMargins(0, 0, 0, 0); 68 | 69 | layout->addStretch(5); 70 | layout->addWidget(this->volume_widget(), 3); 71 | 72 | return widget; 73 | } 74 | 75 | QWidget *MainWindow::volume_widget() 76 | { 77 | QWidget *widget = new QWidget(this); 78 | QHBoxLayout *layout = new QHBoxLayout(widget); 79 | layout->setContentsMargins(0, 0, 0, 0); 80 | 81 | QSlider *slider = new QSlider(Qt::Orientation::Horizontal, widget); 82 | slider->setRange(0, 100); 83 | slider->setSliderPosition(this->config->get_volume()); 84 | update_system_volume(slider->sliderPosition()); 85 | connect(slider, &QSlider::valueChanged, [config = this->config](int position) { 86 | config->set_volume(position); 87 | MainWindow::update_system_volume(position); 88 | }); 89 | 90 | QPushButton *lower_button = new QPushButton(widget); 91 | lower_button->setFlat(true); 92 | lower_button->setIconSize(Theme::icon_32); 93 | this->theme->add_button_icon("volume_mute", lower_button); 94 | connect(lower_button, &QPushButton::clicked, [slider]() { 95 | int position = slider->sliderPosition() - 10; 96 | slider->setSliderPosition(position); 97 | }); 98 | 99 | QPushButton *raise_button = new QPushButton(widget); 100 | raise_button->setFlat(true); 101 | raise_button->setIconSize(Theme::icon_32); 102 | this->theme->add_button_icon("volume_up", raise_button); 103 | connect(raise_button, &QPushButton::clicked, [slider]() { 104 | int position = slider->sliderPosition() + 10; 105 | slider->setSliderPosition(position); 106 | }); 107 | 108 | layout->addWidget(lower_button); 109 | layout->addWidget(slider, 4); 110 | layout->addWidget(raise_button); 111 | 112 | return widget; 113 | } 114 | 115 | void MainWindow::update_system_volume(int position) 116 | { 117 | QProcess *lProc = new QProcess(); 118 | std::string command = "amixer set Master " + std::to_string(position) + "% --quiet"; 119 | lProc->start(QString(command.c_str())); 120 | lProc->waitForFinished(); 121 | } 122 | 123 | void MainWindow::showEvent(QShowEvent *event) 124 | { 125 | QWidget::showEvent(event); 126 | 127 | this->open_auto_tab->start_worker(); 128 | this->open_auto_tab->setFocus(); 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *THIS PROJECT HAS MOVED!* 2 | 3 | Please head over to [dash](https://github.com/openDsh/dash), part of the [openDsh](https://github.com/openDsh) org, for the latest and greatest of this project! 4 | 5 | ![](https://avatars2.githubusercontent.com/u/64765545?s=200&v=4) 6 | 7 | # IntelligentAuto 8 | 9 | IntelligentAuto is a Qt-based infotainment center for your current Linux OpenAuto installation! 10 | 11 | Main features include: 12 | 13 | * volume control 14 | * responsive UI (adjusts to any screen size) 15 | * embedded OpenAuto 16 | * bluetooth media control 17 | * read vehicle OBD-II data 18 | * theming (dark/light mode and accent color) 19 | * brightness (simulated) 20 | 21 | ![home](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/home.png) 22 | 23 | ![openauto_maps](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/openauto_maps.png) 24 | 25 | ![openauto_spotify](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/openauto_spotify.png) 26 | 27 | ![media](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/media.png) 28 | 29 | ![data](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/data.png) 30 | 31 | ![settings](https://github.com/rsjudka/intelligent-auto/blob/master/docs/imgs/settings.png) 32 | 33 | ## Getting Started 34 | 35 | ### Prerequisites 36 | 37 | The following packages have been used while developing this application (NOTE some things may be missing and others are not actually needed): 38 | 39 | * alsa-utils 40 | * cmake 41 | * libboost-all-dev 42 | * libusb-1.0.0-dev 43 | * libssl-dev 44 | * libprotobuf-dev 45 | * protobuf-c-compiler 46 | * protobuf-compiler 47 | * libqt5multimedia5 48 | * libqt5multimedia5-plugins 49 | * libqt5multimediawidgets5 50 | * qtmultimedia5-dev 51 | * libqt5bluetooth5 52 | * libqt5bluetooth5-bin 53 | * qtconnectivity5-dev 54 | * pulseaudio 55 | * librtaudio-dev 56 | * librtaudio6 57 | * libkf5bluezqt-dev 58 | 59 | If you plan on using the Qt video library instead of the OMX library (i.e. not using a Raspberry Pi) you'll also most likely want the following packages: 60 | 61 | * libgstreamer1.0-0 62 | * gstreamer1.0-plugins-base 63 | * gstreamer1.0-plugins-good 64 | * gstreamer1.0-plugins-bad 65 | * gstreamer1.0-plugins-ugly 66 | * gstreamer1.0-libav 67 | * gstreamer1.0-doc 68 | * gstreamer1.0-tools 69 | * gstreamer1.0-x 70 | * gstreamer1.0-alsa 71 | * gstreamer1.0-gl 72 | * gstreamer1.0-gtk3 73 | * gstreamer1.0-qt5 74 | * gstreamer1.0-pulseaudio 75 | 76 | For a Raspberry Pi, you will also need to run the following commands: 77 | ``` 78 | cd /opt/vc/src/hello_pi/libs/ilclient 79 | make 80 | ``` 81 | 82 | ### Building 83 | 84 | It is assumed you have cloned this repo with all submodules and are in the root directory. 85 | 86 | ``` 87 | mkdir build 88 | cd build 89 | cmake -DCMAKE_BUILD_TYPE=Release ../ 90 | make 91 | ``` 92 | 93 | If building this for a Raspberry Pi, add `-DRPI_BUILD=TRUE` to the `cmake` command. 94 | 95 | ### Running 96 | 97 | Building dash will create the `ia` binary in `bin/`. Depending on what you're running it on, you may need to make some adjustments to your system. 98 | 99 | Some things to consider when configuring your system: 100 | 101 | * use a desktop environment that supports transparency (consider using xfce on Raspberry Pi) 102 | * set background color to black and hide any desktop elements (icons, panel, dock, etc.) 103 | * set USB permissions 104 | 105 | ### Gotchas 106 | 107 | Adjusting the brightness does not actually change the screens brightness, it just changes the opacity of the window. 108 | 109 | Authentication of bluetooth connections are not handled in the application (i.e. the first time you are connecting a device). To keep things simple, you could install `blueman` which will prompt you for the necessary actions. If you are still having problems, you may need to try manually authenticating the bluetooth connection. 110 | 111 | There is currently no option for setting the OBD-II interface. Right now it is assumed you have an adapter connected on `/dev/ttyUSB0`. 112 | 113 | Settings are saved periodically every 10 seconds (or anytime the `save` button is clicked). 114 | 115 | Not all OpenAuto settings are acessible. 116 | 117 | If using GStreamer for your video backend (i.e. not a Raspberry Pi), you may get some black bars around the margins of OpenAuto. I'm still trying to figure out a way for it to ignore the aspect ratio. 118 | 119 | ### Future Features/Fixes 120 | 121 | - [ ] add radio player (UI elements exist, just haven't had anything to interface with yet) 122 | - [ ] support bluetooth OBD-II adapter 123 | - [ ] control screen brightness 124 | - [ ] incorporate wireless OpenAuto 125 | - [ ] add wireless hotspot controls 126 | - [ ] add modular OBD-II data tabs 127 | - [ ] add OBD-II error codes tab 128 | - [ ] add dashcam video tab 129 | - [ ] ignore apsect ratio of OpenAuto for GStreamer backend 130 | - [ ] debug issue for OpenAuto randomly disconnecting 131 | - [ ] add automatic light/dark mode 132 | - [ ] add local media player 133 | - [ ] add audio equalizer 134 | -------------------------------------------------------------------------------- /src/app/theme.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | const QFont Theme::font_14 = QFont("Montserrat", 14); 15 | const QFont Theme::font_16 = QFont("Montserrat", 16); 16 | const QFont Theme::font_18 = QFont("Montserrat", 18); 17 | const QFont Theme::font_36 = QFont("Montserrat", 36); 18 | 19 | const QSize Theme::icon_16 = QSize(16, 16); 20 | const QSize Theme::icon_24 = QSize(24, 24); 21 | const QSize Theme::icon_32 = QSize(32, 32); 22 | const QSize Theme::icon_36 = QSize(36, 36); 23 | const QSize Theme::icon_42 = QSize(42, 42); 24 | const QSize Theme::icon_48 = QSize(48, 48); 25 | const QSize Theme::icon_56 = QSize(56, 56); 26 | const QSize Theme::icon_84 = QSize(84, 84); 27 | const QSize Theme::icon_96 = QSize(96, 96); 28 | 29 | const QColor Theme::danger_color = QColor(211, 47, 47); 30 | const QColor Theme::success_color = QColor(56, 142, 60); 31 | 32 | Theme::Theme() : QObject(qApp), palette(), color("blue") 33 | { 34 | QFontDatabase::addApplicationFont(":/fonts/Titillium_Web/TitilliumWeb-Regular.ttf"); 35 | QFontDatabase::addApplicationFont(":/fonts/Montserrat/Montserrat-LightItalic.ttf"); 36 | QFontDatabase::addApplicationFont(":/fonts/Montserrat/Montserrat-Regular.ttf"); 37 | 38 | this->stylesheets["light"] = this->parse_stylesheet(":/stylesheets/light.qss"); 39 | this->stylesheets["dark"] = this->parse_stylesheet(":/stylesheets/dark.qss"); 40 | 41 | qApp->setStyleSheet(this->stylesheets[this->mode ? "dark" : "light"]); 42 | 43 | this->set_palette(); 44 | } 45 | 46 | QString Theme::parse_stylesheet(QString file) 47 | { 48 | QString stylesheet; 49 | 50 | QFile *f = new QFile(file); 51 | f->open(QFile::ReadOnly | QFile::Text); 52 | QTextStream s(f); 53 | stylesheet = s.readAll(); 54 | f->close(); 55 | 56 | delete f; 57 | 58 | return stylesheet; 59 | } 60 | 61 | void Theme::set_palette() 62 | { 63 | QColor new_color(this->colors[this->mode ? "dark" : "light"][this->color]); 64 | this->palette.setColor(QPalette::Base, new_color); 65 | new_color.setAlphaF(.5); 66 | this->palette.setColor(QPalette::AlternateBase, new_color); 67 | qApp->setPalette(this->palette); 68 | } 69 | 70 | QPixmap Theme::create_pixmap_variant(QPixmap &base, qreal opacity) 71 | { 72 | QPixmap image(base.size()); 73 | image.fill(Qt::transparent); 74 | 75 | QPainter painter(&image); 76 | painter.setOpacity(opacity); 77 | painter.drawPixmap(0, 0, base); 78 | painter.end(); 79 | 80 | return image; 81 | } 82 | 83 | void Theme::add_tab_icon(QString name, int index, Qt::Orientation orientation) 84 | { 85 | QTransform t; 86 | t.rotate((orientation == Qt::Orientation::Horizontal) ? 0 : 90); 87 | 88 | QPixmap dark_base = QIcon(QString(":/icons/dark/%1-24px.svg").arg(name)).pixmap(512, 512).transformed(t); 89 | QPixmap dark_active = this->create_pixmap_variant(dark_base, .87); 90 | QPixmap dark_normal = this->create_pixmap_variant(dark_base, .54); 91 | QPixmap dark_disabled = this->create_pixmap_variant(dark_base, .38); 92 | 93 | QIcon dark_icon = QIcon(dark_normal); 94 | dark_icon.addPixmap(dark_active, QIcon::Active, QIcon::On); 95 | dark_icon.addPixmap(dark_disabled, QIcon::Disabled); 96 | this->tab_icons["dark"].append({index, dark_icon}); 97 | 98 | QPixmap light_base = QIcon(QString(":/icons/light/%1-24px.svg").arg(name)).pixmap(512, 512).transformed(t); 99 | QPixmap light_active = this->create_pixmap_variant(light_base, 1); 100 | QPixmap light_normal = this->create_pixmap_variant(light_base, .7); 101 | QPixmap light_disabled = this->create_pixmap_variant(light_base, .5); 102 | 103 | QIcon light_icon = QIcon(light_normal); 104 | light_icon.addPixmap(light_active, QIcon::Active, QIcon::On); 105 | light_icon.addPixmap(light_disabled, QIcon::Disabled); 106 | this->tab_icons["light"].append({index, light_icon}); 107 | 108 | this->update(); 109 | } 110 | 111 | void Theme::add_button_icon(QString name, QPushButton *button, QString normal_name) 112 | { 113 | bool checkable = button->isCheckable(); 114 | 115 | QPixmap dark_base = QIcon(QString(":/icons/dark/%1-24px.svg").arg(name)).pixmap(512, 512); 116 | QPixmap light_base = QIcon(QString(":/icons/light/%1-24px.svg").arg(name)).pixmap(512, 512); 117 | 118 | QPixmap dark_active = this->create_pixmap_variant(dark_base, .87); 119 | QPixmap dark_disabled = this->create_pixmap_variant(dark_base, .38); 120 | 121 | QPixmap light_active = this->create_pixmap_variant(light_base, 1); 122 | QPixmap light_disabled = this->create_pixmap_variant(light_base, .5); 123 | 124 | QPixmap dark_normal; 125 | QPixmap light_normal; 126 | if (normal_name.isNull()) { 127 | dark_normal = checkable ? this->create_pixmap_variant(dark_base, .54) : dark_active; 128 | light_normal = checkable ? this->create_pixmap_variant(light_base, .7) : light_active; 129 | } 130 | else { 131 | QPixmap dark_normal_base = QIcon(QString(":/icons/dark/%1-24px.svg").arg(normal_name)).pixmap(512, 512); 132 | QPixmap light_normal_base = QIcon(QString(":/icons/light/%1-24px.svg").arg(normal_name)).pixmap(512, 512); 133 | 134 | dark_normal = this->create_pixmap_variant(dark_normal_base, .87); 135 | light_normal = this->create_pixmap_variant(light_normal_base, 1); 136 | } 137 | 138 | QIcon dark_icon = QIcon(dark_normal); 139 | dark_icon.addPixmap(dark_active, QIcon::Active, QIcon::On); 140 | dark_icon.addPixmap(dark_disabled, QIcon::Disabled); 141 | this->button_icons["dark"].append({button, dark_icon}); 142 | 143 | QIcon light_icon = QIcon(light_normal); 144 | light_icon.addPixmap(light_active, QIcon::Active, QIcon::On); 145 | light_icon.addPixmap(light_disabled, QIcon::Disabled); 146 | this->button_icons["light"].append({button, light_icon}); 147 | 148 | this->update(); 149 | } 150 | 151 | void Theme::update() 152 | { 153 | this->set_palette(); 154 | qApp->setStyleSheet(this->stylesheets[this->mode ? "dark" : "light"]); 155 | 156 | emit icons_updated(this->tab_icons[this->mode ? "dark" : "light"], 157 | this->button_icons[this->mode ? "dark" : "light"]); 158 | emit color_updated(this->colors[this->mode ? "dark" : "light"]); 159 | } 160 | 161 | Theme *Theme::get_instance() 162 | { 163 | static Theme theme; 164 | return &theme; 165 | } 166 | -------------------------------------------------------------------------------- /src/app/tabs/data.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientation orientation, int rate, 9 | std::vector cmds, int precision, obd_decoder_t decoder, QWidget *parent) 10 | : QWidget(parent) 11 | { 12 | Config *config = Config::get_instance(); 13 | this->si = config->get_si_units(); 14 | 15 | this->rate = rate; 16 | this->precision = precision; 17 | 18 | QBoxLayout *layout; 19 | if (orientation == BOTTOM) 20 | layout = new QVBoxLayout(this); 21 | else 22 | layout = new QHBoxLayout(this); 23 | 24 | QLabel *value_label = new QLabel(this->null_value(), this); 25 | value_label->setFont(value_font); 26 | value_label->setAlignment(Qt::AlignCenter); 27 | 28 | QLabel *unit_label = new QLabel(this->si ? units.second : units.first, this); 29 | unit_label->setFont(unit_font); 30 | unit_label->setAlignment(Qt::AlignCenter); 31 | 32 | this->timer = new QTimer(this); 33 | connect(this->timer, &QTimer::timeout, [this, cmds, decoder, value_label]() { 34 | std::vector results; 35 | for (auto cmd : cmds) { 36 | double result = 0; 37 | if (!OBD::get_instance()->query(cmd, result)) return; 38 | results.push_back(result); 39 | } 40 | 41 | value_label->setText(this->format_value(decoder(results, this->si))); 42 | }); 43 | 44 | connect(config, &Config::si_units_changed, [this, units, unit_label, value_label](bool si) { 45 | this->si = si; 46 | unit_label->setText(this->si ? units.second : units.first); 47 | value_label->setText(this->null_value()); 48 | }); 49 | 50 | layout->addStretch(); 51 | layout->addWidget(value_label); 52 | layout->addWidget(unit_label); 53 | } 54 | 55 | QString Gauge::format_value(double value) 56 | { 57 | if (this->precision == 0) 58 | return QString::number((int)value); 59 | else 60 | return QString::number(value, 'f', this->precision); 61 | } 62 | 63 | QString Gauge::null_value() 64 | { 65 | QString null_str = "-"; 66 | if (this->precision > 0) 67 | null_str += ".-"; 68 | else 69 | null_str += '-'; 70 | 71 | return null_str; 72 | } 73 | 74 | DataTab::DataTab(QWidget *parent) : QWidget(parent) 75 | { 76 | MainWindow *app = qobject_cast(parent); 77 | 78 | QVBoxLayout *layout = new QVBoxLayout(this); 79 | layout->setContentsMargins(0, 0, 0, 0); 80 | 81 | QFrame *status_indicator = new QFrame(this); 82 | status_indicator->setFixedHeight(6); 83 | status_indicator->setAutoFillBackground(true); 84 | connect(app, &MainWindow::set_data_state, [this, status_indicator](bool enabled) { 85 | if (enabled) { 86 | QPalette p(palette()); 87 | if (OBD::get_instance()->is_connected()) { 88 | p.setColor(QPalette::Window, Theme::success_color); 89 | for (auto &gauge : this->gauges) gauge->start(); 90 | } 91 | else { 92 | p.setColor(QPalette::Window, Theme::danger_color); 93 | } 94 | status_indicator->setPalette(p); 95 | } 96 | else { 97 | for (auto &gauge : this->gauges) gauge->stop(); 98 | } 99 | }); 100 | layout->addWidget(status_indicator); 101 | layout->addWidget(this->cluster_widget()); 102 | } 103 | 104 | QWidget *DataTab::cluster_widget() 105 | { 106 | QWidget *widget = new QWidget(this); 107 | QHBoxLayout *layout = new QHBoxLayout(widget); 108 | layout->setContentsMargins(24, 24, 24, 24); 109 | 110 | QWidget *driving_data = this->driving_data_widget(); 111 | layout->addWidget(driving_data); 112 | layout->addWidget(Theme::br(widget, true)); 113 | 114 | QWidget *engine_data = this->engine_data_widget(); 115 | layout->addWidget(engine_data); 116 | 117 | QSizePolicy sp_left(QSizePolicy::Preferred, QSizePolicy::Preferred); 118 | sp_left.setHorizontalStretch(5); 119 | driving_data->setSizePolicy(sp_left); 120 | QSizePolicy sp_right(QSizePolicy::Preferred, QSizePolicy::Preferred); 121 | sp_right.setHorizontalStretch(2); 122 | engine_data->setSizePolicy(sp_right); 123 | 124 | return widget; 125 | } 126 | 127 | QWidget *DataTab::driving_data_widget() 128 | { 129 | QWidget *widget = new QWidget(this); 130 | QVBoxLayout *layout = new QVBoxLayout(widget); 131 | 132 | layout->addStretch(); 133 | layout->addWidget(this->speedo_tach_widget()); 134 | layout->addStretch(); 135 | layout->addWidget(Theme::br(widget)); 136 | layout->addWidget(this->mileage_data_widget()); 137 | 138 | return widget; 139 | } 140 | 141 | QWidget *DataTab::speedo_tach_widget() 142 | { 143 | QWidget *widget = new QWidget(this); 144 | QHBoxLayout *layout = new QHBoxLayout(widget); 145 | 146 | Gauge *speed = new Gauge({"mph", "km/h"}, QFont("Titillium Web", 72), QFont("Montserrat", 16, QFont::Light, true), 147 | Gauge::BOTTOM, 100, {cmds.SPEED}, 0, 148 | [](std::vector x, bool si) { return si ? x[0] : kph_to_mph(x[0]); }, widget); 149 | layout->addWidget(speed); 150 | this->gauges.push_back(speed); 151 | 152 | Gauge *rpm = new Gauge({"x1000rpm", "x1000rpm"}, QFont("Titillium Web", 72), 153 | QFont("Montserrat", 16, QFont::Light, true), Gauge::BOTTOM, 100, {cmds.RPM}, 1, 154 | [](std::vector x, bool _) { return x[0] / 1000.0; }, widget); 155 | layout->addWidget(rpm); 156 | this->gauges.push_back(rpm); 157 | 158 | return widget; 159 | } 160 | 161 | QWidget *DataTab::mileage_data_widget() 162 | { 163 | QWidget *widget = new QWidget(this); 164 | QHBoxLayout *layout = new QHBoxLayout(widget); 165 | 166 | Gauge *mileage = new Gauge({"mpg", "km/L"}, QFont("Titillium Web", 36), QFont("Montserrat", 14, QFont::Light, true), 167 | Gauge::BOTTOM, 100, {cmds.SPEED, cmds.MAF}, 1, 168 | [](std::vector x, bool si) { 169 | return (si ? x[0] : kph_to_mph(x[0])) / (si ? gps_to_lph(x[1]) : gps_to_gph(x[1])); 170 | }, 171 | widget); 172 | layout->addWidget(mileage); 173 | this->gauges.push_back(mileage); 174 | 175 | return widget; 176 | } 177 | 178 | QWidget *DataTab::engine_data_widget() 179 | { 180 | QWidget *widget = new QWidget(this); 181 | QVBoxLayout *layout = new QVBoxLayout(widget); 182 | 183 | layout->addStretch(); 184 | layout->addWidget(this->coolant_temp_widget()); 185 | layout->addStretch(); 186 | layout->addWidget(Theme::br(widget)); 187 | layout->addStretch(); 188 | layout->addWidget(this->engine_load_widget()); 189 | layout->addStretch(); 190 | 191 | return widget; 192 | } 193 | 194 | QWidget *DataTab::coolant_temp_widget() 195 | { 196 | QWidget *widget = new QWidget(this); 197 | QVBoxLayout *layout = new QVBoxLayout(widget); 198 | 199 | Gauge *coolant_temp = new Gauge( 200 | {"°F", "°C"}, QFont("Titillium Web", 36), QFont("Montserrat", 14, QFont::Light, true), Gauge::RIGHT, 5000, 201 | {cmds.COOLANT_TEMP}, 1, [](std::vector x, bool si) { return si ? x[0] : c_to_f(x[0]); }, widget); 202 | layout->addWidget(coolant_temp); 203 | this->gauges.push_back(coolant_temp); 204 | 205 | QLabel *coolant_temp_label = new QLabel("coolant", widget); 206 | coolant_temp_label->setFont(QFont("Montserrat", 14, QFont::Light)); 207 | coolant_temp_label->setAlignment(Qt::AlignHCenter); 208 | layout->addWidget(coolant_temp_label); 209 | 210 | return widget; 211 | } 212 | 213 | QWidget *DataTab::engine_load_widget() 214 | { 215 | QWidget *widget = new QWidget(this); 216 | QVBoxLayout *layout = new QVBoxLayout(widget); 217 | 218 | Gauge *engine_load = 219 | new Gauge({"%", "%"}, QFont("Titillium Web", 36), QFont("Montserrat", 14, QFont::Light, true), Gauge::RIGHT, 220 | 500, {cmds.LOAD}, 1, [](std::vector x, bool _) { return x[0]; }, widget); 221 | layout->addWidget(engine_load); 222 | this->gauges.push_back(engine_load); 223 | 224 | QLabel *engine_load_label = new QLabel("load", widget); 225 | engine_load_label->setFont(QFont("Montserrat", 14, QFont::Light)); 226 | engine_load_label->setAlignment(Qt::AlignHCenter); 227 | layout->addWidget(engine_load_label); 228 | return widget; 229 | } 230 | -------------------------------------------------------------------------------- /src/app/tabs/media.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | MediaTab::MediaTab(QWidget *parent) : QTabWidget(parent) 7 | { 8 | this->tabBar()->setFont(Theme::font_18); 9 | 10 | this->addTab(new RadioPlayerSubTab(this), "Radio"); 11 | this->addTab(new BluetoothPlayerSubTab(this), "Bluetooth"); 12 | } 13 | 14 | BluetoothPlayerSubTab::BluetoothPlayerSubTab(QWidget *parent) : QWidget(parent) 15 | { 16 | this->bluetooth = Bluetooth::get_instance(); 17 | 18 | QVBoxLayout *layout = new QVBoxLayout(this); 19 | layout->setContentsMargins(24, 24, 24, 24); 20 | 21 | layout->addStretch(); 22 | layout->addWidget(this->track_widget()); 23 | layout->addStretch(); 24 | layout->addWidget(this->controls_widget()); 25 | layout->addStretch(); 26 | } 27 | 28 | QWidget *BluetoothPlayerSubTab::track_widget() 29 | { 30 | BluezQt::MediaPlayerPtr media_player = this->bluetooth->get_media_player().second; 31 | 32 | QWidget *widget = new QWidget(this); 33 | QVBoxLayout *layout = new QVBoxLayout(widget); 34 | 35 | QLabel *artist_hdr = new QLabel("Artist", widget); 36 | artist_hdr->setFont(Theme::font_16); 37 | QLabel *artist = new QLabel((media_player != nullptr) ? media_player->track().artist() : QString(), widget); 38 | artist->setFont(Theme::font_14); 39 | artist->setStyleSheet("padding-left: 16px;"); 40 | layout->addWidget(artist_hdr); 41 | layout->addWidget(artist); 42 | 43 | QLabel *album_hdr = new QLabel("Album", widget); 44 | album_hdr->setFont(Theme::font_16); 45 | QLabel *album = new QLabel((media_player != nullptr) ? media_player->track().album() : QString(), widget); 46 | album->setFont(Theme::font_14); 47 | album->setStyleSheet("padding-left: 16px;"); 48 | layout->addWidget(album_hdr); 49 | layout->addWidget(album); 50 | 51 | QLabel *title_hdr = new QLabel("Title", widget); 52 | title_hdr->setFont(Theme::font_16); 53 | QLabel *title = new QLabel((media_player != nullptr) ? media_player->track().title() : QString(), widget); 54 | title->setFont(Theme::font_14); 55 | title->setStyleSheet("padding-left: 16px;"); 56 | layout->addWidget(title_hdr); 57 | layout->addWidget(title); 58 | 59 | connect(this->bluetooth, &Bluetooth::media_player_track_changed, 60 | [artist, album, title](BluezQt::MediaPlayerTrack track) { 61 | artist->setText(track.artist()); 62 | album->setText(track.album()); 63 | title->setText(track.title()); 64 | }); 65 | 66 | return widget; 67 | } 68 | 69 | QWidget *BluetoothPlayerSubTab::controls_widget() 70 | { 71 | BluezQt::MediaPlayerPtr media_player = this->bluetooth->get_media_player().second; 72 | Theme *theme = Theme::get_instance(); 73 | 74 | QWidget *widget = new QWidget(this); 75 | QHBoxLayout *layout = new QHBoxLayout(widget); 76 | 77 | QPushButton *previous_button = new QPushButton(widget); 78 | previous_button->setFlat(true); 79 | previous_button->setIconSize(Theme::icon_84); 80 | connect(previous_button, &QPushButton::clicked, [bluetooth = this->bluetooth]() { 81 | BluezQt::MediaPlayerPtr media_player = bluetooth->get_media_player().second; 82 | if (media_player != nullptr) media_player->previous()->waitForFinished(); 83 | }); 84 | theme->add_button_icon("skip_previous", previous_button); 85 | layout->addStretch(); 86 | layout->addWidget(previous_button); 87 | 88 | QPushButton *play_button = new QPushButton(widget); 89 | play_button->setFlat(true); 90 | play_button->setCheckable(true); 91 | bool status = (media_player != nullptr) ? media_player->status() == BluezQt::MediaPlayer::Status::Playing : false; 92 | play_button->setChecked(status); 93 | play_button->setIconSize(Theme::icon_84); 94 | connect(play_button, &QPushButton::clicked, [bluetooth = this->bluetooth, play_button](bool checked = false) { 95 | play_button->setChecked(!checked); 96 | 97 | BluezQt::MediaPlayerPtr media_player = bluetooth->get_media_player().second; 98 | if (media_player != nullptr) { 99 | if (checked) 100 | media_player->play()->waitForFinished(); 101 | else 102 | media_player->pause()->waitForFinished(); 103 | } 104 | }); 105 | connect(this->bluetooth, &Bluetooth::media_player_status_changed, 106 | [play_button](BluezQt::MediaPlayer::Status status) { 107 | play_button->setChecked(status == BluezQt::MediaPlayer::Status::Playing); 108 | }); 109 | theme->add_button_icon("pause", play_button, "play"); 110 | layout->addStretch(); 111 | layout->addWidget(play_button); 112 | 113 | QPushButton *forward_button = new QPushButton(widget); 114 | forward_button->setFlat(true); 115 | forward_button->setIconSize(Theme::icon_84); 116 | connect(forward_button, &QPushButton::clicked, [bluetooth = this->bluetooth]() { 117 | BluezQt::MediaPlayerPtr media_player = bluetooth->get_media_player().second; 118 | if (media_player != nullptr) media_player->next()->waitForFinished(); 119 | }); 120 | theme->add_button_icon("skip_next", forward_button); 121 | layout->addStretch(); 122 | layout->addWidget(forward_button); 123 | layout->addStretch(); 124 | 125 | return widget; 126 | } 127 | 128 | RadioPlayerSubTab::RadioPlayerSubTab(QWidget *parent) : QWidget(parent) 129 | { 130 | this->theme = Theme::get_instance(); 131 | this->config = Config::get_instance(); 132 | this->tuner = new Tuner(); 133 | 134 | QVBoxLayout *layout = new QVBoxLayout(this); 135 | layout->setContentsMargins(24, 24, 24, 24); 136 | 137 | layout->addStretch(); 138 | layout->addWidget(this->tuner_widget()); 139 | layout->addStretch(); 140 | layout->addWidget(this->controls_widget()); 141 | layout->addStretch(); 142 | } 143 | 144 | QWidget *RadioPlayerSubTab::tuner_widget() 145 | { 146 | QWidget *widget = new QWidget(this); 147 | QVBoxLayout *layout = new QVBoxLayout(widget); 148 | 149 | this->tuner->setParent(widget); 150 | this->tuner->setFont(Theme::font_14); 151 | 152 | QLabel *station = new QLabel(QString::number(this->tuner->sliderPosition() / 10.0, 'f', 1), widget); 153 | station->setAlignment(Qt::AlignCenter); 154 | station->setFont(Theme::font_36); 155 | connect(this->tuner, &Tuner::station_updated, 156 | [station](int freq) { station->setText(QString::number(freq / 10.0, 'f', 1)); }); 157 | layout->addWidget(station); 158 | layout->addStretch(); 159 | layout->addWidget(this->tuner); 160 | 161 | return widget; 162 | } 163 | 164 | QWidget *RadioPlayerSubTab::controls_widget() 165 | { 166 | QWidget *widget = new QWidget(this); 167 | QHBoxLayout *layout = new QHBoxLayout(widget); 168 | 169 | QPushButton *scan_reverse_button = new QPushButton(widget); 170 | scan_reverse_button->setFlat(true); 171 | scan_reverse_button->setIconSize(Theme::icon_84); 172 | this->theme->add_button_icon("fast_rewind", scan_reverse_button); 173 | connect(scan_reverse_button, &QPushButton::clicked, 174 | [tuner = this->tuner]() { tuner->setSliderPosition(tuner->sliderPosition() - 5); }); 175 | layout->addStretch(); 176 | layout->addWidget(scan_reverse_button); 177 | 178 | QPushButton *prev_station_button = new QPushButton(widget); 179 | prev_station_button->setFlat(true); 180 | prev_station_button->setIconSize(Theme::icon_84); 181 | this->theme->add_button_icon("skip_previous", prev_station_button); 182 | connect(prev_station_button, &QPushButton::clicked, 183 | [tuner = this->tuner]() { tuner->setSliderPosition(tuner->sliderPosition() - 1); }); 184 | layout->addStretch(); 185 | layout->addWidget(prev_station_button); 186 | 187 | QPushButton *next_station_button = new QPushButton(widget); 188 | next_station_button->setFlat(true); 189 | next_station_button->setIconSize(Theme::icon_84); 190 | this->theme->add_button_icon("skip_next", next_station_button); 191 | connect(next_station_button, &QPushButton::clicked, 192 | [tuner = this->tuner]() { tuner->setSliderPosition(tuner->sliderPosition() + 1); }); 193 | layout->addStretch(); 194 | layout->addWidget(next_station_button); 195 | 196 | QPushButton *scan_forward_button = new QPushButton(widget); 197 | scan_forward_button->setFlat(true); 198 | scan_forward_button->setIconSize(Theme::icon_84); 199 | this->theme->add_button_icon("fast_forward", scan_forward_button); 200 | connect(scan_forward_button, &QPushButton::clicked, 201 | [tuner = this->tuner]() { tuner->setSliderPosition(tuner->sliderPosition() + 5); }); 202 | layout->addStretch(); 203 | layout->addWidget(scan_forward_button); 204 | 205 | QFrame *vert_break = new QFrame(widget); 206 | vert_break->setLineWidth(1); 207 | vert_break->setFrameShape(QFrame::VLine); 208 | vert_break->setFrameShadow(QFrame::Plain); 209 | layout->addStretch(); 210 | layout->addWidget(vert_break); 211 | 212 | QPushButton *mute_button = new QPushButton(widget); 213 | mute_button->setFlat(true); 214 | mute_button->setCheckable(true); 215 | mute_button->setChecked(this->config->get_radio_muted()); 216 | mute_button->setIconSize(Theme::icon_48); 217 | connect(mute_button, &QPushButton::clicked, 218 | [config = this->config](bool checked = false) { config->set_radio_muted(checked); }); 219 | this->theme->add_button_icon("volume_off", mute_button); 220 | layout->addStretch(); 221 | layout->addWidget(mute_button); 222 | layout->addStretch(); 223 | 224 | return widget; 225 | } 226 | -------------------------------------------------------------------------------- /src/app/tabs/settings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace aasdk = f1x::aasdk; 17 | namespace autoapp = f1x::openauto::autoapp; 18 | 19 | SettingsTab::SettingsTab(QWidget *parent) : QTabWidget(parent) 20 | { 21 | this->tabBar()->setFont(Theme::font_18); 22 | 23 | this->addTab(new GeneralSettingsSubTab(this), "General"); 24 | this->addTab(new BluetoothSettingsSubTab(this), "Bluetooth"); 25 | this->addTab(new OpenAutoSettingsSubTab(this), "OpenAuto"); 26 | } 27 | 28 | GeneralSettingsSubTab::GeneralSettingsSubTab(QWidget *parent) : QWidget(parent) 29 | { 30 | this->theme = Theme::get_instance(); 31 | this->config = Config::get_instance(); 32 | 33 | QVBoxLayout *layout = new QVBoxLayout(this); 34 | layout->setContentsMargins(24, 24, 24, 24); 35 | 36 | layout->addWidget(this->settings_widget()); 37 | layout->addWidget(this->controls_widget()); 38 | } 39 | 40 | QWidget *GeneralSettingsSubTab::settings_widget() 41 | { 42 | QWidget *widget = new QWidget(this); 43 | QVBoxLayout *layout = new QVBoxLayout(widget); 44 | 45 | layout->addWidget(this->dark_mode_row_widget(), 1); 46 | layout->addWidget(this->color_row_widget(), 1); 47 | layout->addWidget(Theme::br(widget), 1); 48 | layout->addWidget(this->si_units_row_widget(), 1); 49 | layout->addWidget(Theme::br(widget), 1); 50 | layout->addWidget(this->brightness_row_widget(), 1); 51 | 52 | return widget; 53 | } 54 | 55 | QWidget *GeneralSettingsSubTab::dark_mode_row_widget() 56 | { 57 | QWidget *widget = new QWidget(this); 58 | QHBoxLayout *layout = new QHBoxLayout(widget); 59 | 60 | QLabel *label = new QLabel("Dark Mode", widget); 61 | label->setFont(Theme::font_16); 62 | layout->addWidget(label, 1); 63 | 64 | Switch *toggle = new Switch(widget); 65 | toggle->setChecked(this->config->get_dark_mode()); 66 | connect(toggle, &Switch::stateChanged, [theme = this->theme, config = this->config](bool state) { 67 | theme->set_mode(state); 68 | config->set_dark_mode(state); 69 | }); 70 | layout->addWidget(toggle, 1, Qt::AlignHCenter); 71 | 72 | return widget; 73 | } 74 | 75 | QWidget *GeneralSettingsSubTab::brightness_row_widget() 76 | { 77 | QWidget *widget = new QWidget(this); 78 | QHBoxLayout *layout = new QHBoxLayout(widget); 79 | 80 | QLabel *label = new QLabel("Brightness", widget); 81 | label->setFont(Theme::font_16); 82 | layout->addWidget(label, 1); 83 | 84 | layout->addWidget(this->brightness_widget(), 1); 85 | 86 | return widget; 87 | } 88 | 89 | QWidget *GeneralSettingsSubTab::brightness_widget() 90 | { 91 | QWidget *widget = new QWidget(this); 92 | QHBoxLayout *layout = new QHBoxLayout(widget); 93 | 94 | QSlider *slider = new QSlider(Qt::Orientation::Horizontal, widget); 95 | slider->setRange(76, 255); 96 | slider->setSliderPosition(this->config->get_brightness()); 97 | connect(slider, &QSlider::valueChanged, 98 | [config = this->config](int position) { config->set_brightness(position); }); 99 | 100 | QPushButton *dim_button = new QPushButton(widget); 101 | dim_button->setFlat(true); 102 | dim_button->setIconSize(Theme::icon_32); 103 | this->theme->add_button_icon("brightness_low", dim_button); 104 | connect(dim_button, &QPushButton::clicked, 105 | [slider]() { slider->setSliderPosition(std::max(76, slider->sliderPosition() - 18)); }); 106 | 107 | QPushButton *brighten_button = new QPushButton(widget); 108 | brighten_button->setFlat(true); 109 | brighten_button->setIconSize(Theme::icon_32); 110 | this->theme->add_button_icon("brightness_high", brighten_button); 111 | connect(brighten_button, &QPushButton::clicked, 112 | [slider]() { slider->setSliderPosition(std::min(255, slider->sliderPosition() + 18)); }); 113 | 114 | layout->addStretch(1); 115 | layout->addWidget(dim_button); 116 | layout->addWidget(slider, 4); 117 | layout->addWidget(brighten_button); 118 | layout->addStretch(1); 119 | 120 | return widget; 121 | } 122 | 123 | QWidget *GeneralSettingsSubTab::si_units_row_widget() 124 | { 125 | QWidget *widget = new QWidget(this); 126 | QHBoxLayout *layout = new QHBoxLayout(widget); 127 | 128 | QLabel *label = new QLabel("SI Units", widget); 129 | label->setFont(Theme::font_16); 130 | layout->addWidget(label, 1); 131 | 132 | Switch *toggle = new Switch(widget); 133 | toggle->setChecked(this->config->get_si_units()); 134 | connect(toggle, &Switch::stateChanged, [config = this->config](bool state) { config->set_si_units(state); }); 135 | layout->addWidget(toggle, 1, Qt::AlignHCenter); 136 | 137 | return widget; 138 | } 139 | 140 | QWidget *GeneralSettingsSubTab::color_row_widget() 141 | { 142 | QWidget *widget = new QWidget(this); 143 | QHBoxLayout *layout = new QHBoxLayout(widget); 144 | 145 | QLabel *label = new QLabel("Color", widget); 146 | label->setFont(Theme::font_16); 147 | layout->addWidget(label, 1); 148 | 149 | QComboBox *box = new QComboBox(widget); 150 | box->setMinimumContentsLength(16); 151 | box->setItemDelegate(new QStyledItemDelegate()); 152 | box->setFont(Theme::font_14); 153 | QMap colors = this->theme->get_colors(); 154 | QMap::iterator it; 155 | QPixmap pixmap(Theme::icon_16); 156 | for (it = colors.begin(); it != colors.end(); it++) { 157 | pixmap.fill(it.value()); 158 | box->addItem(QIcon(pixmap), it.key()); 159 | } 160 | box->setCurrentText(this->config->get_color()); 161 | connect(box, QOverload::of(&QComboBox::activated), 162 | [theme = this->theme, config = this->config](const QString &color) { 163 | theme->set_color(color); 164 | config->set_color(color); 165 | }); 166 | connect(this->theme, &Theme::color_updated, [box](QMap &colors) { 167 | QMap::iterator it; 168 | QPixmap pixmap(Theme::icon_16); 169 | for (it = colors.begin(); it != colors.end(); it++) { 170 | pixmap.fill(it.value()); 171 | box->setItemIcon(box->findText(it.key()), QIcon(pixmap)); 172 | } 173 | }); 174 | layout->addWidget(box, 1, Qt::AlignHCenter); 175 | 176 | return widget; 177 | } 178 | 179 | QWidget *GeneralSettingsSubTab::controls_widget() 180 | { 181 | QWidget *widget = new QWidget(this); 182 | QVBoxLayout *layout = new QVBoxLayout(widget); 183 | 184 | QPushButton *save_button = new QPushButton("save", widget); 185 | save_button->setFont(Theme::font_14); 186 | save_button->setFlat(true); 187 | save_button->setIconSize(Theme::icon_36); 188 | this->theme->add_button_icon("save", save_button); 189 | connect(save_button, &QPushButton::clicked, [config = this->config]() { config->save(); }); 190 | layout->addWidget(save_button, 0, Qt::AlignRight); 191 | 192 | return widget; 193 | } 194 | 195 | BluetoothSettingsSubTab::BluetoothSettingsSubTab(QWidget *parent) : QWidget(parent) 196 | { 197 | this->bluetooth = Bluetooth::get_instance(); 198 | this->theme = Theme::get_instance(); 199 | this->config = Config::get_instance(); 200 | 201 | QHBoxLayout *layout = new QHBoxLayout(this); 202 | layout->setContentsMargins(24, 24, 24, 24); 203 | 204 | layout->addWidget(this->controls_widget()); 205 | layout->addWidget(this->devices_widget()); 206 | } 207 | 208 | QWidget *BluetoothSettingsSubTab::controls_widget() 209 | { 210 | QWidget *widget = new QWidget(this); 211 | QVBoxLayout *layout = new QVBoxLayout(widget); 212 | 213 | QLabel *label = new QLabel("Media Player", widget); 214 | label->setFont(Theme::font_16); 215 | layout->addStretch(); 216 | layout->addWidget(label); 217 | 218 | QLabel *connected_device = new QLabel(this->bluetooth->get_media_player().first, widget); 219 | connected_device->setStyleSheet("padding-left: 16px;"); 220 | connected_device->setFont(Theme::font_14); 221 | connect(this->bluetooth, &Bluetooth::media_player_changed, 222 | [connected_device](QString name, BluezQt::MediaPlayerPtr) { connected_device->setText(name); }); 223 | layout->addWidget(connected_device); 224 | layout->addStretch(); 225 | 226 | layout->addWidget(this->scanner_widget()); 227 | 228 | return widget; 229 | } 230 | 231 | QWidget *BluetoothSettingsSubTab::scanner_widget() 232 | { 233 | QWidget *widget = new QWidget(this); 234 | QHBoxLayout *layout = new QHBoxLayout(widget); 235 | 236 | QPushButton *button = new QPushButton("scan", widget); 237 | button->setFont(Theme::font_14); 238 | button->setFlat(true); 239 | button->setEnabled(this->bluetooth->has_adapter()); 240 | button->setIconSize(Theme::icon_36); 241 | this->theme->add_button_icon("bluetooth_searching", button); 242 | connect(button, &QPushButton::clicked, [bluetooth = this->bluetooth]() { bluetooth->scan(); }); 243 | layout->addWidget(button); 244 | 245 | ProgressIndicator *loader = new ProgressIndicator(widget); 246 | connect(this->bluetooth, &Bluetooth::scan_status, [button, loader](bool status) { 247 | button->setEnabled(!status); 248 | if (status) 249 | loader->startAnimation(); 250 | else 251 | loader->stopAnimation(); 252 | }); 253 | layout->addWidget(loader); 254 | 255 | return widget; 256 | } 257 | 258 | QWidget *BluetoothSettingsSubTab::devices_widget() 259 | { 260 | QWidget *widget = new QWidget(this); 261 | QVBoxLayout *layout = new QVBoxLayout(widget); 262 | 263 | for (BluezQt::DevicePtr device : this->bluetooth->get_devices()) { 264 | if (device->address() == this->config->get_bluetooth_device()) device->connectToDevice()->waitForFinished(); 265 | QPushButton *button = new QPushButton(device->name(), widget); 266 | button->setFont(Theme::font_16); 267 | button->setCheckable(true); 268 | if (device->isConnected()) button->setChecked(true); 269 | connect(button, &QPushButton::clicked, [config = this->config, button, device](bool checked = false) { 270 | button->setChecked(!checked); 271 | if (checked) { 272 | device->connectToDevice()->waitForFinished(); 273 | config->set_bluetooth_device(device->address()); 274 | } 275 | else { 276 | device->disconnectFromDevice()->waitForFinished(); 277 | config->set_bluetooth_device(QString()); 278 | } 279 | }); 280 | 281 | this->devices[device] = button; 282 | layout->addWidget(button); 283 | } 284 | connect(this->bluetooth, &Bluetooth::device_added, [this, layout, widget](BluezQt::DevicePtr device) { 285 | QPushButton *button = new QPushButton(device->name(), widget); 286 | button->setFont(Theme::font_16); 287 | button->setCheckable(true); 288 | if (device->isConnected()) button->setChecked(true); 289 | connect(button, &QPushButton::clicked, [button, device](bool checked = false) { 290 | button->setChecked(!checked); 291 | if (checked) 292 | device->connectToDevice()->waitForFinished(); 293 | else 294 | device->disconnectFromDevice()->waitForFinished(); 295 | }); 296 | this->devices[device] = button; 297 | layout->addWidget(button); 298 | }); 299 | connect(this->bluetooth, &Bluetooth::device_changed, [this](BluezQt::DevicePtr device) { 300 | this->devices[device]->setText(device->name()); 301 | this->devices[device]->setChecked(device->isConnected()); 302 | }); 303 | connect(this->bluetooth, &Bluetooth::device_removed, [this, layout](BluezQt::DevicePtr device) { 304 | layout->removeWidget(devices[device]); 305 | delete this->devices[device]; 306 | this->devices.remove(device); 307 | }); 308 | 309 | return widget; 310 | } 311 | 312 | OpenAutoSettingsSubTab::OpenAutoSettingsSubTab(QWidget *parent) : QWidget(parent) 313 | { 314 | this->bluetooth = Bluetooth::get_instance(); 315 | this->theme = Theme::get_instance(); 316 | this->config = Config::get_instance(); 317 | 318 | QVBoxLayout *layout = new QVBoxLayout(this); 319 | layout->setContentsMargins(24, 24, 24, 24); 320 | 321 | layout->addWidget(this->settings_widget()); 322 | } 323 | 324 | QWidget *OpenAutoSettingsSubTab::settings_widget() 325 | { 326 | QWidget *widget = new QWidget(this); 327 | QVBoxLayout *layout = new QVBoxLayout(widget); 328 | 329 | layout->addWidget(this->rhd_row_widget(), 1); 330 | layout->addWidget(Theme::br(widget), 1); 331 | layout->addWidget(this->frame_rate_row_widget(), 1); 332 | layout->addWidget(this->resolution_row_widget(), 1); 333 | layout->addWidget(this->dpi_row_widget(), 1); 334 | layout->addWidget(Theme::br(widget), 1); 335 | layout->addWidget(this->rt_audio_row_widget(), 1); 336 | layout->addWidget(this->audio_channels_row_widget(), 1); 337 | layout->addWidget(Theme::br(widget), 1); 338 | layout->addWidget(this->bluetooth_row_widget(), 1); 339 | 340 | return widget; 341 | } 342 | 343 | QWidget *OpenAutoSettingsSubTab::rhd_row_widget() 344 | { 345 | QWidget *widget = new QWidget(this); 346 | QHBoxLayout *layout = new QHBoxLayout(widget); 347 | 348 | QLabel *label = new QLabel("Right-Hand-Drive", widget); 349 | label->setFont(Theme::font_16); 350 | layout->addWidget(label, 1); 351 | 352 | Switch *toggle = new Switch(widget); 353 | toggle->setChecked(this->config->open_auto_config->getHandednessOfTrafficType() == 354 | autoapp::configuration::HandednessOfTrafficType::RIGHT_HAND_DRIVE); 355 | connect(toggle, &Switch::stateChanged, [config = this->config](bool state) { 356 | config->open_auto_config->setHandednessOfTrafficType( 357 | state ? autoapp::configuration::HandednessOfTrafficType::RIGHT_HAND_DRIVE 358 | : autoapp::configuration::HandednessOfTrafficType::LEFT_HAND_DRIVE); 359 | }); 360 | layout->addWidget(toggle, 1, Qt::AlignHCenter); 361 | 362 | return widget; 363 | } 364 | 365 | QWidget *OpenAutoSettingsSubTab::frame_rate_row_widget() 366 | { 367 | QWidget *widget = new QWidget(this); 368 | QHBoxLayout *layout = new QHBoxLayout(widget); 369 | 370 | QLabel *label = new QLabel("Frame Rate", widget); 371 | label->setFont(Theme::font_16); 372 | layout->addWidget(label, 1); 373 | 374 | QGroupBox *group = new QGroupBox(widget); 375 | QHBoxLayout *group_layout = new QHBoxLayout(group); 376 | 377 | QRadioButton *fps_30_button = new QRadioButton("30fps", group); 378 | fps_30_button->setFixedHeight(fps_30_button->height()); 379 | fps_30_button->setFont(Theme::font_14); 380 | fps_30_button->setChecked(this->config->open_auto_config->getVideoFPS() == aasdk::proto::enums::VideoFPS::_30); 381 | connect(fps_30_button, &QRadioButton::clicked, 382 | [config = this->config]() { config->open_auto_config->setVideoFPS(aasdk::proto::enums::VideoFPS::_30); }); 383 | group_layout->addWidget(fps_30_button); 384 | 385 | QRadioButton *fps_60_button = new QRadioButton("60fps", group); 386 | fps_60_button->setFixedHeight(fps_60_button->height()); 387 | fps_60_button->setFont(Theme::font_14); 388 | fps_60_button->setChecked(this->config->open_auto_config->getVideoFPS() == aasdk::proto::enums::VideoFPS::_60); 389 | connect(fps_60_button, &QRadioButton::clicked, 390 | [config = this->config]() { config->open_auto_config->setVideoFPS(aasdk::proto::enums::VideoFPS::_60); }); 391 | group_layout->addWidget(fps_60_button); 392 | 393 | layout->addWidget(group, 1, Qt::AlignHCenter); 394 | 395 | return widget; 396 | } 397 | 398 | QWidget *OpenAutoSettingsSubTab::resolution_row_widget() 399 | { 400 | QWidget *widget = new QWidget(this); 401 | QHBoxLayout *layout = new QHBoxLayout(widget); 402 | 403 | QLabel *label = new QLabel("Resolution", widget); 404 | label->setFont(Theme::font_16); 405 | layout->addWidget(label, 1); 406 | 407 | QGroupBox *group = new QGroupBox(widget); 408 | QHBoxLayout *group_layout = new QHBoxLayout(group); 409 | 410 | QRadioButton *res_480_button = new QRadioButton("480p", group); 411 | res_480_button->setFixedHeight(res_480_button->height()); 412 | res_480_button->setFont(Theme::font_14); 413 | res_480_button->setChecked(this->config->open_auto_config->getVideoResolution() == 414 | aasdk::proto::enums::VideoResolution::_480p); 415 | connect(res_480_button, &QRadioButton::clicked, [config = this->config]() { 416 | config->open_auto_config->setVideoResolution(aasdk::proto::enums::VideoResolution::_480p); 417 | }); 418 | group_layout->addWidget(res_480_button); 419 | 420 | QRadioButton *res_720_button = new QRadioButton("720p", group); 421 | res_720_button->setFixedHeight(res_720_button->height()); 422 | res_720_button->setFont(Theme::font_14); 423 | res_720_button->setChecked(this->config->open_auto_config->getVideoResolution() == 424 | aasdk::proto::enums::VideoResolution::_720p); 425 | connect(res_720_button, &QRadioButton::clicked, [config = this->config]() { 426 | config->open_auto_config->setVideoResolution(aasdk::proto::enums::VideoResolution::_720p); 427 | }); 428 | group_layout->addWidget(res_720_button); 429 | 430 | QRadioButton *res_1080_button = new QRadioButton("1080p", group); 431 | res_1080_button->setFixedHeight(res_1080_button->height()); 432 | res_1080_button->setFont(Theme::font_14); 433 | res_1080_button->setChecked(this->config->open_auto_config->getVideoResolution() == 434 | aasdk::proto::enums::VideoResolution::_1080p); 435 | connect(res_1080_button, &QRadioButton::clicked, [config = this->config]() { 436 | config->open_auto_config->setVideoResolution(aasdk::proto::enums::VideoResolution::_1080p); 437 | }); 438 | group_layout->addWidget(res_1080_button); 439 | 440 | layout->addWidget(group, 1, Qt::AlignHCenter); 441 | 442 | return widget; 443 | } 444 | 445 | QWidget *OpenAutoSettingsSubTab::dpi_row_widget() 446 | { 447 | QWidget *widget = new QWidget(this); 448 | QHBoxLayout *layout = new QHBoxLayout(widget); 449 | 450 | QLabel *label = new QLabel("DPI", widget); 451 | label->setFont(Theme::font_16); 452 | layout->addWidget(label, 1); 453 | 454 | layout->addWidget(this->dpi_widget(), 1); 455 | 456 | return widget; 457 | } 458 | 459 | QWidget *OpenAutoSettingsSubTab::dpi_widget() 460 | { 461 | QWidget *widget = new QWidget(this); 462 | QHBoxLayout *layout = new QHBoxLayout(widget); 463 | 464 | QSlider *slider = new QSlider(Qt::Orientation::Horizontal, widget); 465 | slider->setFixedHeight(slider->height()); 466 | slider->setRange(0, 512); 467 | slider->setSliderPosition(this->config->open_auto_config->getScreenDPI()); 468 | QLabel *value = new QLabel(QString::number(slider->sliderPosition()), widget); 469 | value->setFixedHeight(value->height()); 470 | value->setFont(Theme::font_14); 471 | connect(slider, &QSlider::valueChanged, [config = this->config, value](int position) { 472 | value->setText(QString::number(position)); 473 | config->open_auto_config->setScreenDPI(position); 474 | }); 475 | 476 | layout->addStretch(2); 477 | layout->addWidget(slider, 4); 478 | layout->addWidget(value, 1); 479 | layout->addStretch(1); 480 | 481 | return widget; 482 | } 483 | 484 | QWidget *OpenAutoSettingsSubTab::rt_audio_row_widget() 485 | { 486 | QWidget *widget = new QWidget(this); 487 | QHBoxLayout *layout = new QHBoxLayout(widget); 488 | 489 | QLabel *label = new QLabel("RtAudio", widget); 490 | label->setFont(Theme::font_16); 491 | layout->addWidget(label, 1); 492 | 493 | Switch *toggle = new Switch(widget); 494 | toggle->setChecked(this->config->open_auto_config->getAudioOutputBackendType() == 495 | autoapp::configuration::AudioOutputBackendType::RTAUDIO); 496 | connect(toggle, &Switch::stateChanged, [config = this->config](bool state) { 497 | config->open_auto_config->setAudioOutputBackendType( 498 | state ? autoapp::configuration::AudioOutputBackendType::RTAUDIO 499 | : autoapp::configuration::AudioOutputBackendType::QT); 500 | }); 501 | layout->addWidget(toggle, 1, Qt::AlignHCenter); 502 | 503 | return widget; 504 | } 505 | 506 | QWidget *OpenAutoSettingsSubTab::audio_channels_row_widget() 507 | { 508 | QWidget *widget = new QWidget(this); 509 | QHBoxLayout *layout = new QHBoxLayout(widget); 510 | 511 | QLabel *label = new QLabel("Audio Channels", widget); 512 | label->setFont(Theme::font_16); 513 | layout->addWidget(label, 1); 514 | 515 | QGroupBox *group = new QGroupBox(widget); 516 | QHBoxLayout *group_layout = new QHBoxLayout(group); 517 | 518 | QCheckBox *music_button = new QCheckBox("Music", group); 519 | music_button->setFixedHeight(music_button->height()); 520 | music_button->setFont(Theme::font_14); 521 | music_button->setChecked(this->config->open_auto_config->musicAudioChannelEnabled()); 522 | connect(music_button, &QCheckBox::toggled, 523 | [config = this->config](bool checked) { config->open_auto_config->setMusicAudioChannelEnabled(checked); }); 524 | group_layout->addWidget(music_button); 525 | group_layout->addStretch(2); 526 | 527 | QCheckBox *speech_button = new QCheckBox("Speech", group); 528 | speech_button->setFixedHeight(speech_button->height()); 529 | speech_button->setFont(Theme::font_14); 530 | speech_button->setChecked(this->config->open_auto_config->speechAudioChannelEnabled()); 531 | connect(speech_button, &QCheckBox::toggled, 532 | [config = this->config](bool checked) { config->open_auto_config->setSpeechAudioChannelEnabled(checked); }); 533 | group_layout->addWidget(speech_button); 534 | 535 | layout->addWidget(group, 1, Qt::AlignHCenter); 536 | 537 | return widget; 538 | } 539 | 540 | QWidget *OpenAutoSettingsSubTab::bluetooth_row_widget() 541 | { 542 | QWidget *widget = new QWidget(this); 543 | QHBoxLayout *layout = new QHBoxLayout(widget); 544 | 545 | QLabel *label = new QLabel("Bluetooth", widget); 546 | label->setFont(Theme::font_16); 547 | layout->addWidget(label, 1); 548 | 549 | Switch *toggle = new Switch(widget); 550 | toggle->setChecked(this->config->open_auto_config->getBluetoothAdapterType() == 551 | autoapp::configuration::BluetoothAdapterType::LOCAL); 552 | connect(toggle, &Switch::stateChanged, [config = this->config](bool state) { 553 | config->open_auto_config->setBluetoothAdapterType(state ? autoapp::configuration::BluetoothAdapterType::LOCAL 554 | : autoapp::configuration::BluetoothAdapterType::NONE); 555 | }); 556 | layout->addWidget(toggle, 1, Qt::AlignHCenter); 557 | 558 | return widget; 559 | } 560 | --------------------------------------------------------------------------------