diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index fbda02b7..b3b4251d 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -224,59 +224,6 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() { }; } -/* -SetPlayDeadView::SetPlayDeadView(NavigationView& nav) { - add_children({ - &text_sequence, - &button_enter, - &button_cancel - }); - - button_enter.on_select = [this, &nav](Button&){ - if (!entermode) { - sequence = 0; - keycount = 0; - memset(sequence_txt, '-', 10); - text_sequence.set(sequence_txt); - entermode = true; - button_cancel.hidden(true); - set_dirty(); - } else { - if (sequence == 0x8D1) // U D L R - nav.display_modal("Warning", "Default sequence entered !", ABORT, nullptr); - else { - persistent_memory::set_playdead_sequence(sequence); - nav.pop(); - } - } - }; - - button_enter.on_dir = [this](Button&, KeyEvent key){ - if ((entermode == true) && (keycount < 10)) { - key_code = static_cast::type>(key); - if (key_code == 0) - sequence_txt[keycount] = 'R'; - else if (key_code == 1) - sequence_txt[keycount] = 'L'; - else if (key_code == 2) - sequence_txt[keycount] = 'D'; - else if (key_code == 3) - sequence_txt[keycount] = 'U'; - text_sequence.set(sequence_txt); - sequence = (sequence << 3) | (key_code + 1); - keycount++; - return true; - } - return false; - }; - - button_cancel.on_select = [&nav](Button&){ nav.pop(); }; -} - -void SetPlayDeadView::focus() { - button_cancel.focus(); -} -*/ SetUIView::SetUIView(NavigationView& nav) { add_children({ @@ -293,7 +240,6 @@ SetUIView::SetUIView(NavigationView& nav) { checkbox_speaker.set_value(persistent_memory::config_speaker()); checkbox_showsplash.set_value(persistent_memory::config_splash()); checkbox_showclock.set_value(!persistent_memory::hide_clock()); - //checkbox_login.set_value(persistent_memory::config_login()); uint32_t backlight_timer = persistent_memory::config_backlight_timer(); if (backlight_timer) { @@ -345,209 +291,58 @@ SetAudioView::SetAudioView(NavigationView& nav) { add_children({ &labels, &field_tone_mix, - &button_ok + &button_save, + &button_cancel }); field_tone_mix.set_value(persistent_memory::tone_mix()); - button_ok.on_select = [&nav, this](Button&) { + button_save.on_select = [&nav, this](Button&) { persistent_memory::set_tone_mix(field_tone_mix.value()); nav.pop(); }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; } void SetAudioView::focus() { - field_tone_mix.focus(); + button_save.focus(); } -/*void ModInfoView::on_show() { - if (modules_nb) update_infos(0); -} +SetQRCodeView::SetQRCodeView(NavigationView& nav) { + add_children({ + &checkbox_bigger_qr, + &button_save, + &button_cancel + }); -void ModInfoView::update_infos(uint8_t modn) { - char info_str[27]; - char ch; - uint8_t c; - Point pos = { 0, 0 }; - Rect rect = { { 16, 144 }, { 208, 144 } }; + checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code()); - info_str[0] = 0; - strcat(info_str, module_list[modn].name); - text_namestr.set(info_str); - - info_str[0] = 0; - strcat(info_str, to_string_dec_uint(module_list[modn].size).c_str()); - strcat(info_str, " bytes"); - text_sizestr.set(info_str); - - info_str[0] = 0; - for (c = 0; c < 8; c++) - strcat(info_str, to_string_hex(module_list[modn].md5[c], 2).c_str()); - text_md5_a.set(info_str); - - info_str[0] = 0; - for (c = 8; c < 16; c++) - strcat(info_str, to_string_hex(module_list[modn].md5[c], 2).c_str()); - text_md5_b.set(info_str); - - // TODO: Use ui_console - display.fill_rectangle(rect, Color::black()); - - const Font& font = font::fixed_8x16; - const auto line_height = font.line_height(); - c = 0; - while((ch = module_list[modn].description[c++])) { - const auto glyph = font.glyph(ch); - const auto advance = glyph.advance(); - if((pos.x + advance.x) > rect.width()) { - pos.x = 0; - pos.y += line_height; - } - const Point pos_glyph { - static_cast(rect.pos.x + pos.x), - static_cast(rect.pos.y + pos.y) - }; - display.draw_glyph(pos_glyph, glyph, Color::white(), Color::black()); - pos.x += advance.x; - } -} - -ModInfoView::ModInfoView(NavigationView& nav) { - const char magic[4] = {'P', 'P', 'M', ' '}; - UINT bw; - uint8_t i; - char read_buf[16]; - char info_str[25]; - FILINFO modinfo; - FIL modfile; - DIR rootdir; - FRESULT res; - uint8_t c; - - using option_t = std::pair; - using options_t = std::vector; - option_t opt; - options_t opts; - - static constexpr Style style_orange { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::orange(), - }; - - add_children({{ - &text_modcount, - &text_name, - &text_namestr, - &text_size, - &text_sizestr, - &text_md5, - &text_md5_a, - &text_md5_b, - &button_ok - }}); - - text_name.set_style(&style_orange); - text_size.set_style(&style_orange); - text_md5.set_style(&style_orange); - - // TODO: Find a way to merge this with m4_load_image() in m4_startup.cpp - - // Scan SD card root directory for files starting with the magic bytes - c = 0; - if (f_opendir(&rootdir, "/") == FR_OK) { - for (;;) { - res = f_readdir(&rootdir, &modinfo); - if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort - // Only care about files with .bin extension - if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) { - f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ); - // Magic bytes check - f_read(&modfile, &read_buf, 4, &bw); - for (i = 0; i < 4; i++) - if (read_buf[i] != magic[i]) break; - if (i == 4) { - memcpy(&module_list[c].filename, modinfo.fname, 8); - module_list[c].filename[8] = 0; - - f_lseek(&modfile, 4); - f_read(&modfile, &module_list[c].version, 2, &bw); - f_lseek(&modfile, 6); - f_read(&modfile, &module_list[c].size, 4, &bw); - f_lseek(&modfile, 10); - f_read(&modfile, &module_list[c].name, 16, &bw); - f_lseek(&modfile, 26); - f_read(&modfile, &module_list[c].md5, 16, &bw); - f_lseek(&modfile, 42); - f_read(&modfile, &module_list[c].description, 214, &bw); - f_lseek(&modfile, 256); - - // Sanitize - module_list[c].name[15] = 0; - module_list[c].description[213] = 0; - - memcpy(info_str, module_list[c].filename, 16); - strcat(info_str, "(V"); - strcat(info_str, to_string_dec_uint(module_list[c].version, 4, '0').c_str()); - strcat(info_str, ")"); - while(strlen(info_str) < 24) - strcat(info_str, " "); - - opt = std::make_pair(info_str, c); - opts.insert(opts.end(), opt); - - c++; - } - f_close(&modfile); - } - if (c == 8) break; - } - } - f_closedir(&rootdir); - - modules_nb = c; - - if (modules_nb) { - strcpy(info_str, "Found "); - strcat(info_str, to_string_dec_uint(modules_nb, 1).c_str()); - strcat(info_str, " module(s)"); - - text_modcount.set(info_str); - option_modules.set_options(opts); - - add_child(&option_modules); - - option_modules.on_change = [this](size_t n, OptionsField::value_t v) { - (void)n; - update_infos(v); - }; - } else { - strcpy(info_str, "No modules found"); - text_modcount.set(info_str); - } - - button_ok.on_select = [&nav,this](Button&){ + button_save.on_select = [&nav, this](Button&) { + persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value()); nav.pop(); }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; + } -void ModInfoView::focus() { - if (modules_nb) - option_modules.focus(); - else - button_ok.focus(); -}*/ +void SetQRCodeView::focus() { + button_save.focus(); +} SettingsMenuView::SettingsMenuView(NavigationView& nav) { add_items({ - //{ "..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav](){ nav.pop(); } }, - { "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, - { "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push(); } }, + { "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, + { "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push(); } }, { "Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push(); } }, - //{ "SD card modules", ui::Color::dark_cyan(), [&nav](){ nav.push(); } }, - { "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push(); } }, + { "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push(); } }, { "Touchscreen", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push(); } }, - //{ "Play dead", ui::Color::dark_cyan(), &bitmap_icon_playdead, [&nav](){ nav.push(); } } + { "QR code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav](){ nav.push(); } } }); set_max_rows(2); // allow wider buttons } diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 30acd820..8aaad391 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -219,12 +219,7 @@ public: std::string title() const override { return "UI settings"; }; private: - /*Checkbox checkbox_login { - { 3 * 8, 2 * 16 }, - 20, - "Login with play dead" - };*/ - + Checkbox checkbox_speaker { { 3 * 8, 4 * 16 }, 20, @@ -298,111 +293,43 @@ private: '0' }; - Button button_ok { + Button button_save { { 2 * 8, 16 * 16, 12 * 8, 32 }, "Save" }; + + Button button_cancel { + { 16 * 8, 16 * 16, 12 * 8, 32 }, + "Cancel", + }; }; -/* -class SetPlayDeadView : public View { -public: - SetPlayDeadView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Playdead settings"; }; - -private: - bool entermode = false; - uint32_t sequence { 0 }; - uint8_t keycount { 0 }, key_code { }; - char sequence_txt[11] = { 0 }; - - Text text_sequence { - { 64, 32, 14 * 8, 16 }, - "Enter sequence", - }; - - Button button_enter { - { 16, 192, 96, 24 }, - "Enter" - }; - Button button_cancel { - { 128, 192, 96, 24 }, - "Cancel" - }; -};*/ -/*class ModInfoView : public View { +class SetQRCodeView : public View { public: - ModInfoView(NavigationView& nav); + SetQRCodeView(NavigationView& nav); + void focus() override; - void on_show() override; + + std::string title() const override { return "QR code settings"; }; private: - void update_infos(uint8_t modn); - - typedef struct moduleinfo_t{ - char filename[9]; - uint16_t version; - uint32_t size; - char md5[16]; - char name[16]; - char description[214]; - } moduleinfo_t; - - moduleinfo_t module_list[8]; // 8 max for now - - uint8_t modules_nb; - - Text text_modcount { - { 2 * 8, 1 * 16, 18 * 8, 16 }, - "Searching..." + Checkbox checkbox_bigger_qr { + { 3 * 8, 9 * 16 }, + 20, + "Show large QR code" }; - OptionsField option_modules { - { 2 * 8, 2 * 16 }, - 24, - { - { "-", 0 } - } + Button button_save { + { 2 * 8, 16 * 16, 12 * 8, 32 }, + "Save" }; - Text text_name { - { 2 * 8, 4 * 16, 5 * 8, 16 }, - "Name:" + Button button_cancel { + { 16 * 8, 16 * 16, 12 * 8, 32 }, + "Cancel", }; - Text text_namestr { - { 8 * 8, 4 * 16, 16 * 8, 16 }, - "..." - }; - Text text_size { - { 2 * 8, 5 * 16, 5 * 8, 16 }, - "Size:" - }; - Text text_sizestr { - { 8 * 8, 5 * 16, 16 * 8, 16 }, - "..." - }; - Text text_md5 { - { 2 * 8, 6 * 16, 4 * 8, 16 }, - "MD5:" - }; - Text text_md5_a { - { 7 * 8, 6 * 16, 16 * 8, 16 }, - "..." - }; - Text text_md5_b { - { 7 * 8, 7 * 16, 16 * 8, 16 }, - "..." - }; - - Button button_ok { - { 4 * 8, 272, 64, 24 }, - "Ok" - }; -};*/ +}; class SettingsMenuView : public BtnGridView { public: diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 8cb98a43..57c9f5fe 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -1285,6 +1285,28 @@ static constexpr Bitmap bitmap_icon_previous { { 16, 16 }, bitmap_icon_previous_data }; +static constexpr uint8_t bitmap_icon_qr_code_data[] = { + 0x00, 0x00, + 0xFE, 0x7E, + 0xC6, 0x62, + 0xFA, 0x5A, + 0xFA, 0x5A, + 0xDA, 0x5A, + 0xFE, 0x7E, + 0x7E, 0x7E, + 0x00, 0x00, + 0xFE, 0x46, + 0xC2, 0x06, + 0xFA, 0x18, + 0xFA, 0x18, + 0xC6, 0x60, + 0xFE, 0x62, + 0x00, 0x00, +}; +static constexpr Bitmap bitmap_icon_qr_code { + { 16, 16 }, bitmap_icon_qr_code_data +}; + static constexpr uint8_t bitmap_icon_rds_data[] = { 0x00, 0x00, 0x00, 0x00, diff --git a/firmware/application/ui/ui_qrcode.cpp b/firmware/application/ui/ui_qrcode.cpp index fd18d990..f824b151 100644 --- a/firmware/application/ui/ui_qrcode.cpp +++ b/firmware/application/ui/ui_qrcode.cpp @@ -23,6 +23,7 @@ #include "ui_qrcode.hpp" #include "qrcodegen.hpp" #include "portapack.hpp" +#include "portapack_persistent_memory.hpp" #include #include @@ -43,25 +44,56 @@ QRCodeImage::QRCodeImage( } void QRCodeImage::paint(Painter& painter) { + + // The structure to manage the QR code QRCode qrcode; - int qr_version = 10; // bigger versions aren't handled very well - - // Allocate a chunk of memory to store the QR code - uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; - - qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); - display.fill_rectangle(Rect(92, 97, 63, 63), Color::white()); + //Either small or large QR code can be shown.. - for (uint8_t y = 0; y < qrcode.size; y++) { - for (uint8_t x = 0; x < qrcode.size; x++) { - if (qrcode_getModule(&qrcode, x, y)) { - display.draw_pixel(Point(95+x,100+y), Color::black()); + if(portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code + int qr_version = 2; + + // Allocate a chunk of memory to store the QR code + uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; + + qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); + + + display.fill_rectangle(Rect(10, 30, 220, 220), Color::white()); + + for (uint8_t y = 0; y < qrcode.size; y++) { + for (uint8_t x = 0; x < qrcode.size; x++) { + if (qrcode_getModule(&qrcode, x, y)) { + display.fill_rectangle(Rect(20+(x*8), 40+(y*8), 8, 8), Color::black()); + + } + } + } + + } + + else { // show small QR code + int qr_version = 10; + + // Allocate a chunk of memory to store the QR code + uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)]; + + qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_); + + + display.fill_rectangle(Rect(92, 97, 63, 63), Color::white()); + + for (uint8_t y = 0; y < qrcode.size; y++) { + for (uint8_t x = 0; x < qrcode.size; x++) { + if (qrcode_getModule(&qrcode, x, y)) { + display.draw_pixel(Point(95+x,100+y), Color::black()); + + } + } + } - } - } } } @@ -84,10 +116,9 @@ QRCodeView::QRCodeView( add_children({ - &text_qr, &qr_code, &button_close}); - text_qr.set(qr_text); + //text_qr.set(qr_text); qr_code.set_text(qr_text); button_close.on_select = [&nav](Button&){ diff --git a/firmware/application/ui/ui_qrcode.hpp b/firmware/application/ui/ui_qrcode.hpp index 43dab2d8..4d110775 100644 --- a/firmware/application/ui/ui_qrcode.hpp +++ b/firmware/application/ui/ui_qrcode.hpp @@ -74,13 +74,13 @@ private: { 50, 100, 100, 100 } }; - Text text_qr { - { 0 * 8, 10 * 16, 32 * 8, 1 * 8 }, - "-" - }; + //Text text_qr { + // { 0 * 8, 10 * 16, 32 * 8, 1 * 8 }, + // "-" + //}; Button button_close { - { 9 * 8, 15 * 16, 12 * 8, 3 * 16 }, + { 9 * 8, 31 * 8, 12 * 8, 3 * 16 }, "Back" }; }; diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index c4d957b0..c1427b7d 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -230,6 +230,10 @@ void set_playdead_sequence(const uint32_t new_value) { // bits 31, 30,29,28,27, 26, 25 stores the different single bit configs depicted below // bits on position 4 to 19 (16 bits) store the clkout frequency +bool show_bigger_qr_code() { // show bigger QR code + return data->ui_config & (1 << 23); +} + bool hide_clock() { // clock hidden from main menu return data->ui_config & (1 << 25); } @@ -266,6 +270,10 @@ uint32_t config_backlight_timer() { return timer_seconds[data->ui_config & 7]; //first three bits, 8 possible values } +void set_show_bigger_qr_code(bool v) { + data->ui_config = (data->ui_config & ~(1 << 23)) | (v << 23); +} + void set_clock_hidden(bool v) { data->ui_config = (data->ui_config & ~(1 << 25)) | (v << 25); } diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index d04b1239..529cc98e 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -78,6 +78,7 @@ uint8_t config_cpld(); void set_config_cpld(uint8_t i); bool config_splash(); +bool show_bigger_qr_code(); bool hide_clock(); bool clock_with_date(); bool config_login(); @@ -85,6 +86,7 @@ bool config_speaker(); uint32_t config_backlight_timer(); void set_config_splash(bool v); +void set_show_bigger_qr_code(bool v); void set_clock_hidden(bool v); void set_clock_with_date(bool v); void set_config_login(bool v); diff --git a/firmware/graphics/icon_qr_code.png b/firmware/graphics/icon_qr_code.png new file mode 100755 index 00000000..c19e9bdb Binary files /dev/null and b/firmware/graphics/icon_qr_code.png differ