Procházet zdrojové kódy

simple menu to change version, ecc

Bob Matcuk před 3 roky
rodič
revize
0d6d173426
3 změnil soubory, kde provedl 158 přidání a 26 odebrání
  1. 15 5
      README.md
  2. 1 1
      application.fam
  3. 142 20
      qrcode_app.c

+ 15 - 5
README.md

@@ -71,8 +71,17 @@ Select one using the arrow keys and the center button. The qrcode will display.
 If you push the right arrow, some stats will display: the qrcode "Version" -
 which corresponds to how big it is; the ECC level - which determines the
 qrcode's resilience to damage, such as a dirty screen (Low, Medium, Quartile,
-and High); and the qrcode Mode (Numeric, Alpha-Numeric, Binary, or Kanji). You
-can hide the stats by pressing the left arrow.
+and High); and the qrcode Mode (Numeric, Alpha-Numeric, Binary, or Kanji).
+
+While viewing the stats, you can select Version or ECC using the up and down
+arrows and the center button. You can then increase or decrease the Version or
+ECC using up and down and save your choice using the center buttton. This
+feature was mostly added for my own amusement and testing, but, theoretically,
+it may help a reader that's having trouble if the default ECC is less than the
+highest value ("H"): you can increase the Version by 1 and then set the ECC to
+"H". Whether or not this helps depends on the reader.
+
+You can hide the stats by pressing the left arrow.
 
 When you're done viewing the qrcode, press the back button to return to the
 file browser. If you push the back button in the file browser, the app will
@@ -80,10 +89,11 @@ exit.
 
 I will ask that you temper your expectations: the Flipper Zero screen is small
 and many readers may have difficulty reading the qrcodes, especially if they
-are encoding a lot of data. I have successfully got my iPhone to read qrcodes
-encoding phone numbers and wifi info, both of which are relatively short.
+are encoding a lot of data. However, I have successfully got my iPhone to read
+qrcodes encoding phone numbers, wifi info, and a url, all the way up to a
+version 11 qrcode (ie, the largest size the screen will fit).
 
-## Wifi QRCodes
+## Example: Wifi QRCodes
 Most phones can automatically connect to wifi networks from a qrcode. If you
 should like to encode your wifi's connection info into a qrcode, here's how
 you'd do it:

+ 1 - 1
application.fam

@@ -1,7 +1,7 @@
 App(
     appid="qrcode",
     name="qrcode",
-    fap_version=(1,0),
+    fap_version=(1,1),
     fap_description="Display qrcodes",
     fap_author="Bob Matcuk",
     fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode",

+ 142 - 20
qrcode_app.c

@@ -54,10 +54,17 @@ typedef struct {
     ViewPort* view_port;
 
     FuriMutex** mutex;
+    FuriString* message;
     QRCode* qrcode;
+    uint8_t min_version;
+    uint8_t max_ecc_at_min_version;
     bool loading;
     bool too_long;
     bool show_stats;
+    uint8_t selected_idx;
+    bool edit;
+    uint8_t set_version;
+    uint8_t set_ecc;
 } QRCodeApp;
 
 /**
@@ -107,11 +114,13 @@ static void render_callback(Canvas* canvas, void* ctx) {
     uint8_t font_height = canvas_current_font_height(canvas);
     uint8_t width = canvas_width(canvas);
     uint8_t height = canvas_height(canvas);
-    if (instance->qrcode) {
+    if (instance->loading) {
+        canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
+    } else if (instance->qrcode) {
         uint8_t size = instance->qrcode->size;
         uint8_t pixel_size = height / size;
         uint8_t top = (height - pixel_size * size) / 2;
-        uint8_t left = instance->show_stats ? top : (width - pixel_size * size) / 2;
+        uint8_t left = ((instance->show_stats ? 65 : width) - pixel_size * size) / 2;
         for (uint8_t y = 0; y < size; y++) {
             for (uint8_t x = 0; x < size; x++) {
                 if (qrcode_getModule(instance->qrcode, x, y)) {
@@ -125,24 +134,44 @@ static void render_callback(Canvas* canvas, void* ctx) {
         }
 
         if (instance->show_stats) {
-            if (top < 2) top = 2;
-            left += size * pixel_size + top;
+            top = 10;
+            left = 66;
 
             FuriString* str = furi_string_alloc();
 
-            furi_string_printf(str, "Ver: %i", instance->qrcode->version);
-            canvas_draw_str(canvas, left, top + font_height, furi_string_get_cstr(str));
+            if (!instance->edit || instance->selected_idx == 0) {
+                furi_string_printf(str, "Ver: %i", instance->set_version);
+                canvas_draw_str(canvas, left + 5, top + font_height, furi_string_get_cstr(str));
+                if (instance->selected_idx == 0) {
+                    canvas_draw_triangle(canvas, left, top + font_height / 2, font_height - 4, 4, CanvasDirectionLeftToRight);
+                }
+                if (instance->edit) {
+                    uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "Ver: 8") / 2;
+                    canvas_draw_triangle(canvas, arrow_left, top, font_height - 4, 4, CanvasDirectionBottomToTop);
+                    canvas_draw_triangle(canvas, arrow_left, top + font_height + 1, font_height - 4, 4, CanvasDirectionTopToBottom);
+                }
+            }
 
-            furi_string_printf(str, "ECC: %c", get_ecc_char(instance->qrcode->ecc));
-            canvas_draw_str(canvas, left, 2 * (top + font_height), furi_string_get_cstr(str));
+            if (!instance->edit || instance->selected_idx == 1) {
+                furi_string_printf(str, "ECC: %c", get_ecc_char(instance->set_ecc));
+                canvas_draw_str(canvas, left + 5, 2 * font_height + top + 2, furi_string_get_cstr(str));
+                if (instance->selected_idx == 1) {
+                    canvas_draw_triangle(canvas, left, 3 * font_height / 2 + top + 2, font_height - 4, 4, CanvasDirectionLeftToRight);
+                }
+                if (instance->edit) {
+                    uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "ECC: H") / 2;
+                    canvas_draw_triangle(canvas, arrow_left, font_height + top + 2, font_height - 4, 4, CanvasDirectionBottomToTop);
+                    canvas_draw_triangle(canvas, arrow_left, 2 * font_height + top + 3, font_height - 4, 4, CanvasDirectionTopToBottom);
+                }
+            }
 
-            furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
-            canvas_draw_str(canvas, left, 3 * (top + font_height), furi_string_get_cstr(str));
+            if (!instance->edit) {
+                furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
+                canvas_draw_str(canvas, left + 5, 3 * font_height + top + 4, furi_string_get_cstr(str));
+            }
 
             furi_string_free(str);
         }
-    } else if (instance->loading) {
-        canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
     } else {
         uint8_t margin = (height - font_height * 2) / 3;
         canvas_draw_str_aligned(canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode.");
@@ -230,6 +259,36 @@ static void qrcode_free(QRCode* qrcode) {
     free(qrcode);
 }
 
+/**
+ * Rebuild the qrcode. Assumes that instance->message is the message to encode,
+ * that the mutex has been acquired, and the specified version/ecc will be
+ * sufficiently large enough to encode the full message. It is also assumed
+ * that the old qrcode will be free'd by the caller.
+ * @param instance The qrcode app instance
+ * @param version The qrcode version to use
+ * @param ecc The qrcode ECC level to use
+ * @returns true if the qrcode was successfully created
+ */
+static bool rebuild_qrcode(QRCodeApp* instance, uint8_t version, uint8_t ecc) {
+    furi_assert(instance);
+    furi_assert(instance->message);
+
+    const char* cstr = furi_string_get_cstr(instance->message);
+    uint16_t len = strlen(cstr);
+    instance->qrcode = qrcode_alloc(version);
+
+    int8_t res = qrcode_initBytes(instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len);
+    if (res != 0) {
+        FURI_LOG_E(TAG, "Could not create qrcode");
+
+        qrcode_free(instance->qrcode);
+        instance->qrcode = NULL;
+
+        return false;
+    }
+    return true;
+}
+
 /**
  * Load a qrcode from a string
  * @param instance The qrcode app instance
@@ -241,18 +300,30 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
     furi_assert(str);
 
     furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
+    if (instance->message) {
+        furi_string_free(instance->message);
+        instance->message = NULL;
+    }
     if (instance->qrcode) {
         qrcode_free(instance->qrcode);
         instance->qrcode = NULL;
     }
     instance->too_long = false;
     instance->show_stats = false;
+    instance->selected_idx = 0;
+    instance->edit = false;
 
     bool result = false;
     do {
         const char* cstr = furi_string_get_cstr(str);
         uint16_t len = strlen(cstr);
 
+        instance->message = furi_string_alloc_set(str);
+        if (!instance->message) {
+            FURI_LOG_E(TAG, "Could not allocate message");
+            break;
+        }
+
         // figure out the qrcode "mode"
         uint8_t mode = MODE_BYTE;
         if      (is_numeric(cstr, len))      mode = MODE_NUMERIC;
@@ -285,17 +356,14 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
         version++;
 
         // Build the qrcode
-        instance->qrcode = qrcode_alloc(version);
-        int8_t res = qrcode_initBytes(instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len);
-        if (res != 0) {
-            FURI_LOG_E(TAG, "Could not create qrcode");
-
-            qrcode_free(instance->qrcode);
-            instance->qrcode = NULL;
-
+        if (!rebuild_qrcode(instance, version, ecc)) {
+            furi_string_free(instance->message);
+            instance->message = NULL;
             break;
         }
 
+        instance->min_version = instance->set_version = version;
+        instance->max_ecc_at_min_version = instance->set_ecc = ecc;
         result = true;
     } while (false);
 
@@ -370,10 +438,13 @@ static QRCodeApp* qrcode_app_alloc() {
 
     instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 
+    instance->message = NULL;
     instance->qrcode = NULL;
     instance->loading = true;
     instance->too_long = false;
     instance->show_stats = false;
+    instance->selected_idx = 0;
+    instance->edit = false;
 
     return instance;
 }
@@ -383,6 +454,7 @@ static QRCodeApp* qrcode_app_alloc() {
  * @param qrcode_app The app to free
  */
 static void qrcode_app_free(QRCodeApp* instance) {
+    if (instance->message) furi_string_free(instance->message);
     if (instance->qrcode) qrcode_free(instance->qrcode);
 
     gui_remove_view_port(instance->gui, instance->view_port);
@@ -433,17 +505,67 @@ int32_t qrcode_app(void* p) {
             furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
 
             if (input.key == InputKeyBack) {
+                if (instance->message) {
+                    furi_string_free(instance->message);
+                    instance->message = NULL;
+                }
                 if (instance->qrcode) {
                     qrcode_free(instance->qrcode);
                     instance->qrcode = NULL;
                 }
                 instance->loading = true;
+                instance->edit = false;
                 furi_mutex_release(instance->mutex);
                 break;
             } else if (input.key == InputKeyRight) {
                 instance->show_stats = true;
             } else if (input.key == InputKeyLeft) {
                 instance->show_stats = false;
+            } else if (instance->show_stats && !instance->loading && instance->qrcode) {
+                if (input.key == InputKeyUp) {
+                    if (!instance->edit) {
+                        instance->selected_idx = MAX(0, instance->selected_idx - 1);
+                    } else {
+                        if (instance->selected_idx == 0 && instance->set_version < MAX_QRCODE_VERSION) {
+                            instance->set_version++;
+                        } else if (instance->selected_idx == 1) {
+                            uint8_t max_ecc = instance->set_version == instance->min_version ? instance->max_ecc_at_min_version : ECC_HIGH;
+                            if (instance->set_ecc < max_ecc) {
+                                instance->set_ecc++;
+                            }
+                        }
+                    }
+                } else if (input.key == InputKeyDown) {
+                    if (!instance->edit) {
+                        instance->selected_idx = MIN(1, instance->selected_idx + 1);
+                    } else {
+                        if (instance->selected_idx == 0 && instance->set_version > instance->min_version) {
+                            instance->set_version--;
+                            if (instance->set_version == instance->min_version) {
+                                instance->set_ecc = MAX(instance->set_ecc, instance->max_ecc_at_min_version);
+                            }
+                        } else if (instance->selected_idx == 1 && instance->set_ecc > 0) {
+                            instance->set_ecc--;
+                        }
+                    }
+                } else if (input.key == InputKeyOk) {
+                    if (instance->edit && (instance->set_version != instance->qrcode->version || instance->set_ecc != instance->qrcode->ecc)) {
+                        QRCode* qrcode = instance->qrcode;
+                        instance->loading = true;
+
+                        if (rebuild_qrcode(instance, instance->set_version, instance->set_ecc)) {
+                            qrcode_free(qrcode);
+                        } else {
+                            FURI_LOG_E(TAG, "Could not rebuild qrcode");
+                            instance->qrcode = qrcode;
+                            instance->set_version = qrcode->version;
+                            instance->set_ecc = qrcode->ecc;
+                        }
+
+                        instance->loading = false;
+                    }
+                    instance->edit = !instance->edit;
+                }
             }
 
             furi_mutex_release(instance->mutex);