Преглед изворни кода

Add cntdown_timer from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: cntdown_timer
git-subtree-mainline: e3b57af2f1ddc6844a9743884db23fda1bc26311
git-subtree-split: 377da10e4cd8dba7050209827f5f06e0e45137ea
Willy-JL пре 2 година
родитељ
комит
41adbe3c11

+ 1 - 0
cntdown_timer/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev apps_source_code/fpz_cntdown_timer-main

+ 11 - 0
cntdown_timer/README.md

@@ -0,0 +1,11 @@
+## Simple count down timer application for flipper zero
+
+### How to use
+`up/down`: set second/minute/hour value.
+
+`ok`: start/stop counting.
+
+`long press on ok`: stop counting and reset counter.
+
+`left/right`: select second/minute/hour value.
+

+ 71 - 0
cntdown_timer/app.c

@@ -0,0 +1,71 @@
+#include "views/countdown_view.h"
+#include "app.h"
+
+static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid);
+
+int32_t app_main(void* p) {
+    UNUSED(p);
+
+    CountDownTimerApp* app = countdown_app_new();
+
+    countdown_app_run(app);
+
+    countdown_app_delete(app);
+
+    return 0;
+}
+
+static uint32_t view_exit(void* ctx) {
+    furi_assert(ctx);
+
+    return VIEW_NONE;
+}
+
+CountDownTimerApp* countdown_app_new(void) {
+    CountDownTimerApp* app = (CountDownTimerApp*)(malloc(sizeof(CountDownTimerApp)));
+
+    // 1.1 open gui
+    app->gui = furi_record_open(RECORD_GUI);
+
+    // 2.1 setup view dispatcher
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+
+    // 2.2 attach view dispatcher to gui
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    // 2.3 attach views to the dispatcher
+    // helloworld view
+    app->helloworld_view = countdown_timer_view_new();
+    register_view(app->view_dispatcher, countdown_timer_view_get_view(app->helloworld_view), 0xff);
+
+    // 2.5 switch to default view
+    view_dispatcher_switch_to_view(app->view_dispatcher, 0xff);
+
+    return app;
+}
+
+void countdown_app_delete(CountDownTimerApp* app) {
+    furi_assert(app);
+
+    // delete views
+    view_dispatcher_remove_view(app->view_dispatcher, 0xff);
+    countdown_timer_view_delete(app->helloworld_view); // hello world view
+
+    // delete view dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+    furi_record_close(RECORD_GUI);
+
+    // self
+    free(app);
+}
+
+void countdown_app_run(CountDownTimerApp* app) {
+    view_dispatcher_run(app->view_dispatcher);
+}
+
+static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid) {
+    view_dispatcher_add_view(dispatcher, viewid, view);
+
+    view_set_previous_callback(view, view_exit);
+}

+ 22 - 0
cntdown_timer/app.h

@@ -0,0 +1,22 @@
+#ifndef __APP_H__
+#define __APP_H__
+
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+
+// app
+typedef struct {
+    Gui* gui; // gui object
+    ViewDispatcher* view_dispatcher; // view dispacther of the gui
+
+    // views
+    CountDownTimView* helloworld_view;
+
+} CountDownTimerApp;
+
+CountDownTimerApp* countdown_app_new(void);
+void countdown_app_delete(CountDownTimerApp* app);
+void countdown_app_run(CountDownTimerApp* app);
+
+#endif

+ 20 - 0
cntdown_timer/application.fam

@@ -0,0 +1,20 @@
+# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
+
+App(
+    appid="cntdown_tim",
+    name="Count Down Timer",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="app_main",
+    cdefines=["APP_COUNT_DOWN_TIMER"],
+    requires=[
+        "gui",
+    ],
+    stack_size=2 * 1024,
+    order=20,
+    fap_icon="cntdown_timer.png",
+    fap_category="Tools",
+    fap_author="@0w0mewo",
+    fap_weburl="https://github.com/0w0mewo/fpz_cntdown_timer",
+    fap_version="1.2",
+    fap_description="Simple count down timer",
+)

BIN
cntdown_timer/cntdown_timer.png


BIN
cntdown_timer/img/1.png


+ 34 - 0
cntdown_timer/utils/utils.c

@@ -0,0 +1,34 @@
+#include <furi.h>
+#include "utils.h"
+
+static const NotificationSequence sequence_beep = {
+    &message_blue_255,
+    &message_note_d5,
+    &message_delay_100,
+    &message_sound_off,
+
+    NULL,
+};
+
+void notification_beep_once() {
+    notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_beep);
+    notification_off();
+}
+
+void notification_off() {
+    furi_record_close(RECORD_NOTIFICATION);
+}
+
+void notification_timeup() {
+    notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_audiovisual_alert);
+}
+
+void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec) {
+    snprintf(
+        buffer,
+        len,
+        "%02ld:%02ld:%02ld",
+        (sec % (60 * 60 * 24)) / (60 * 60), // hour
+        (sec % (60 * 60)) / 60, // minute
+        sec % 60); // second
+}

+ 12 - 0
cntdown_timer/utils/utils.h

@@ -0,0 +1,12 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__
+#include <furi.h>
+#include <notification/notification_messages.h>
+
+void notification_beep_once();
+void notification_off();
+void notification_timeup();
+
+void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec);
+
+#endif // __UTILS_H__

+ 349 - 0
cntdown_timer/views/countdown_view.c

@@ -0,0 +1,349 @@
+#include "countdown_view.h"
+#include "../utils/utils.h"
+
+// internal
+static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd);
+static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd);
+static void handle_time_setting_select(InputKey key, CountDownTimView* cdv);
+static void draw_selection(Canvas* canvas, CountDownViewSelect selection);
+
+static void countdown_timer_start_counting(CountDownTimView* cdv);
+static void countdown_timer_pause_counting(CountDownTimView* cdv);
+
+// callbacks
+static void countdown_timer_view_on_enter(void* ctx);
+static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx);
+static bool countdown_timer_view_on_input(InputEvent* event, void* ctx);
+static void timer_cb(void* ctx);
+
+CountDownTimView* countdown_timer_view_new() {
+    CountDownTimView* cdv = (CountDownTimView*)(malloc(sizeof(CountDownTimView)));
+
+    cdv->view = view_alloc();
+
+    cdv->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, cdv);
+
+    cdv->counting = false;
+
+    view_set_context(cdv->view, cdv);
+
+    view_allocate_model(cdv->view, ViewModelTypeLocking, sizeof(CountDownModel));
+
+    view_set_draw_callback(cdv->view, countdown_timer_view_on_draw);
+    view_set_input_callback(cdv->view, countdown_timer_view_on_input);
+    view_set_enter_callback(cdv->view, countdown_timer_view_on_enter);
+
+    return cdv;
+}
+
+void countdown_timer_view_delete(CountDownTimView* cdv) {
+    furi_assert(cdv);
+
+    view_free(cdv->view);
+    furi_timer_stop(cdv->timer);
+    furi_timer_free(cdv->timer);
+
+    free(cdv);
+}
+
+View* countdown_timer_view_get_view(CountDownTimView* cdv) {
+    return cdv->view;
+}
+
+void countdown_timer_view_state_reset(CountDownTimView* cdv) {
+    cdv->counting = false;
+
+    with_view_model(
+        cdv->view, CountDownModel * model, { model->count = model->saved_count_setting; }, true)
+}
+
+void countdown_timer_state_toggle(CountDownTimView* cdv) {
+    bool on = cdv->counting;
+    if(!on) {
+        countdown_timer_start_counting(cdv);
+    } else {
+        countdown_timer_pause_counting(cdv);
+    }
+
+    cdv->counting = !on;
+}
+
+// on enter callback, CountDownTimView as ctx
+static void countdown_timer_view_on_enter(void* ctx) {
+    furi_assert(ctx);
+
+    CountDownTimView* cdv = (CountDownTimView*)ctx;
+
+    // set current count to a initial value
+    with_view_model(
+        cdv->view,
+        CountDownModel * model,
+        {
+            model->count = INIT_COUNT;
+            model->saved_count_setting = INIT_COUNT;
+        },
+        true);
+}
+
+// view draw callback, CountDownModel as ctx
+static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx) {
+    furi_assert(ctx);
+    CountDownModel* model = (CountDownModel*)ctx;
+
+    char buffer[64];
+
+    int32_t count = model->count;
+    int32_t expected_count = model->saved_count_setting;
+
+    CountDownViewSelect select = model->select;
+
+    // elements_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+
+    canvas_set_font(canvas, FontBigNumbers);
+    draw_selection(canvas, select);
+
+    parse_sec_to_time_str(buffer, sizeof(buffer), count);
+    canvas_draw_str_aligned(
+        canvas, SCREEN_CENTER_X, SCREEN_CENTER_Y, AlignCenter, AlignCenter, buffer);
+
+    elements_progress_bar(canvas, 0, 0, SCREEN_WIDTH, (1.0 * count / expected_count));
+}
+
+// keys input event callback, CountDownTimView as ctx
+static bool countdown_timer_view_on_input(InputEvent* event, void* ctx) {
+    furi_assert(ctx);
+
+    CountDownTimView* hw = (CountDownTimView*)ctx;
+
+    if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
+        switch(event->key) {
+        case InputKeyUp:
+        case InputKeyDown:
+        case InputKeyRight:
+        case InputKeyLeft:
+            handle_time_setting_select(event->key, hw);
+            break;
+
+        case InputKeyOk:
+            if(event->type == InputTypeShort) {
+                handle_misc_cmd(hw, CountDownTimerToggleCounting);
+            }
+            break;
+        case InputKeyBack:
+            return false;
+            break;
+
+        default:
+            break;
+        }
+
+        return true;
+    }
+
+    if(event->type == InputTypeLong) {
+        switch(event->key) {
+        case InputKeyOk:
+            handle_misc_cmd(hw, CountDownTimerReset);
+            break;
+
+        case InputKeyBack:
+            return false;
+            break;
+
+        default:
+            break;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+static void timer_cb(void* ctx) {
+    furi_assert(ctx);
+
+    CountDownTimView* cdv = (CountDownTimView*)ctx;
+
+    int32_t count;
+    bool timeup = false;
+
+    // decrement counter
+    with_view_model(
+        cdv->view,
+        CountDownModel * model,
+        {
+            count = model->count;
+            count--;
+
+            // check timeup
+            if(count <= 0) {
+                count = 0;
+                timeup = true;
+            }
+
+            model->count = count;
+        },
+        true);
+
+    if(timeup) {
+        handle_misc_cmd(cdv, CountDownTimerTimeUp);
+    }
+}
+
+static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd) {
+    int32_t count;
+
+    with_view_model(
+        cdv->view,
+        CountDownModel * model,
+        {
+            count = model->count;
+            switch(cmd) {
+            case CountDownTimerMinuteUp:
+                count += 60;
+                break;
+            case CountDownTimerMinuteDown:
+                count -= 60;
+                break;
+            case CountDownTimerHourDown:
+                count -= 3600;
+                break;
+            case CountDownTimerHourUp:
+                count += 3600;
+                break;
+            case CountDownTimerSecUp:
+                count++;
+                break;
+            case CountDownTimerSecDown:
+                count--;
+                break;
+            default:
+                break;
+            }
+
+            if(count < 0) {
+                count = 0;
+            }
+
+            // update count state
+            model->count = count;
+
+            // save the count time setting
+            model->saved_count_setting = count;
+        },
+        true);
+}
+
+static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd) {
+    switch(cmd) {
+    case CountDownTimerTimeUp:
+        notification_timeup();
+        break;
+
+    case CountDownTimerReset:
+        furi_timer_stop(hw->timer);
+        countdown_timer_view_state_reset(hw);
+        notification_off();
+
+        break;
+
+    case CountDownTimerToggleCounting:
+        countdown_timer_state_toggle(hw);
+        break;
+
+    default:
+        break;
+    }
+
+    return;
+}
+
+static void handle_time_setting_select(InputKey key, CountDownTimView* cdv) {
+    bool counting = cdv->counting;
+    CountDownViewCmd setting_cmd = CountDownTimerSecUp;
+    CountDownViewSelect selection;
+
+    if(counting) {
+        return;
+    }
+
+    // load current selection from model context
+    with_view_model(
+        cdv->view, CountDownModel * model, { selection = model->select; }, false);
+
+    // select
+    switch(key) {
+    case InputKeyUp:
+        switch(selection) {
+        case CountDownTimerSelectSec:
+            setting_cmd = CountDownTimerSecUp;
+            break;
+        case CountDownTimerSelectMinute:
+            setting_cmd = CountDownTimerMinuteUp;
+            break;
+        case CountDownTimerSelectHour:
+            setting_cmd = CountDownTimerHourUp;
+            break;
+        }
+
+        handle_time_setting_updown(cdv, setting_cmd);
+        break;
+
+    case InputKeyDown:
+        switch(selection) {
+        case CountDownTimerSelectSec:
+            setting_cmd = CountDownTimerSecDown;
+            break;
+        case CountDownTimerSelectMinute:
+            setting_cmd = CountDownTimerMinuteDown;
+            break;
+        case CountDownTimerSelectHour:
+            setting_cmd = CountDownTimerHourDown;
+            break;
+        }
+
+        handle_time_setting_updown(cdv, setting_cmd);
+        break;
+
+    case InputKeyRight:
+        selection--;
+        selection = selection % 3;
+        break;
+
+    case InputKeyLeft:
+        selection++;
+        selection = selection % 3;
+        break;
+
+    default:
+        break;
+    }
+
+    // save selection to model context
+    with_view_model(
+        cdv->view, CountDownModel * model, { model->select = selection; }, false);
+}
+
+static void draw_selection(Canvas* canvas, CountDownViewSelect selection) {
+    switch(selection) {
+    case CountDownTimerSelectSec:
+        elements_slightly_rounded_box(canvas, SCREEN_CENTER_X + 25, SCREEN_CENTER_Y + 11, 24, 2);
+        break;
+    case CountDownTimerSelectMinute:
+        elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 10, SCREEN_CENTER_Y + 11, 21, 2);
+        break;
+    case CountDownTimerSelectHour:
+        elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 47, SCREEN_CENTER_Y + 11, 24, 2);
+        break;
+    }
+}
+
+static void countdown_timer_start_counting(CountDownTimView* cdv) {
+    furi_timer_start(cdv->timer, furi_kernel_get_tick_frequency() * 1); // 1s
+}
+
+static void countdown_timer_pause_counting(CountDownTimView* cdv) {
+    furi_timer_stop(cdv->timer);
+    notification_off();
+}

+ 59 - 0
cntdown_timer/views/countdown_view.h

@@ -0,0 +1,59 @@
+#ifndef __COUNTDOWN_VIEW_H__
+#define __COUNTDOWN_VIEW_H__
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/view.h>
+#include <gui/elements.h>
+
+#define SCREEN_WIDTH 128
+#define SCREEN_HEIGHT 64
+#define SCREEN_CENTER_X (SCREEN_WIDTH / 2)
+#define SCREEN_CENTER_Y (SCREEN_HEIGHT / 2)
+
+#define INIT_COUNT 10
+
+typedef enum {
+    CountDownTimerMinuteUp,
+    CountDownTimerMinuteDown,
+    CountDownTimerSecDown,
+    CountDownTimerSecUp,
+    CountDownTimerHourUp,
+    CountDownTimerHourDown,
+    CountDownTimerReset,
+    CountDownTimerTimeUp,
+    CountDownTimerToggleCounting,
+} CountDownViewCmd;
+
+typedef enum {
+    CountDownTimerSelectSec,
+    CountDownTimerSelectMinute,
+    CountDownTimerSelectHour,
+} CountDownViewSelect;
+
+typedef struct {
+    int32_t count;
+    int32_t saved_count_setting;
+    CountDownViewSelect select; // setting
+} CountDownModel;
+
+typedef struct {
+    View* view;
+    FuriTimer* timer; // 1Hz tick timer
+    bool counting;
+
+} CountDownTimView;
+
+// functions
+// allocate helloworld view
+CountDownTimView* countdown_timer_view_new();
+
+// delete helloworld view
+void countdown_timer_view_delete(CountDownTimView* cdv);
+
+// return view
+View* countdown_timer_view_get_view(CountDownTimView* cdv);
+
+void countdown_timer_view_state_reset(CountDownTimView* cdv); // set initial state
+void countdown_timer_state_toggle(CountDownTimView* cdv);
+#endif // __COUNTDOWN_VIEW_H__