iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
Security

Flipper Zero 宇宙最強攻略:30 天帶你從入門到入坑系列 第 29

Day29 - 絶対的な優位を掌握せよ!倒数24時間、登場する圧軸必殺技!

  • 分享至 

  • xImage
  •  

Cover

https://ithelp.ithome.com.tw/upload/images/20241013/20168875zQGIq9cbl0.png
圖片來源:https://instantiator.dev/post/flipper-zero-app-tutorial-02/

資安倫理宣導

請注意,透過 Flipper Zero 學習的資訊技術與知識,目的在於提升個人的技術能力和資安意識。我們強烈呼籲大家,絕對不要使用所學知識從事任何違法行為。您的合法使用是我們的期望,也是您自身責任的
一部分。

定義場景功能的核心步驟

當我們在開發 Flipper Zero 應用程式時,每個場景都有三個關鍵函式,這些函式主要是負責管理進入場景時的資源、處理事件以及離開場景時釋放資源,如下:

  1. *_on_enter:當進入場景時初始化視圖及資源。
  2. *_on_event:處理使用者輸入和自定義事件。
  3. *_on_exit:離開場景時,釋放佔用的資源。

設定主選單的回傳函式

在主選單的場景中,我們會定義一個回傳函式 test_app_menu_callback_main_menu。這個回傳函式會接收到使用者的選擇,這個選擇值來自於 TestAppMenuSelection。透過這個值,我們可以確認使用者選擇了哪一個選項,並將此選擇發送到場景管理器的自定義事件處理函式。這樣做的好處是將事件傳遞給場景管理器,而不是在選單回傳函式中處理事件,能夠確保每個場景都能處理自己的邏輯,提升整體的可讀性和維護性。

進入主選單場景

當我們進入主選單場景時,會調用 test_app_scene_on_enter_main_menu 函式來設定場景。這裡的工作是初始化該場景所需要的資源,並指示視圖調度器切換到對應的視圖。步驟如下:

  1. 重設選單視圖

    menu_reset(app->menu);
    

    這行程式碼會重置選單,確保我們開始時的選單是乾淨的,不包含任何之前添加的項目。

  2. 添加選單項目
    每個選單項目都會用來表示一個選擇,並且會指派一個來自 TestAppMenuSelection 的 ID。這個 ID 在選單回呼函式中用來確認使用者做出的選擇。例如:

    menu_add_item(
        app->menu,
        "First popup",
        NULL,
        TestAppMenuSelection_One,
        test_app_menu_callback_main_menu,
        app);
    

    在這裡,我們為選單添加了一個名為「First popup」的選項,並指定當使用者選擇這個項目時會調用 test_app_menu_callback_main_menu,並且將選擇項目標識(TestAppMenuSelection_One)傳給這個回呼函式。

  3. 切換到選單視圖
    最後一步是指示視圖調度器切換到選單視圖,讓使用者能夠看到並進行選擇:

    view_dispatcher_switch_to_view(app->view_dispatcher, TestAppView_Menu);
    

到目前為止,我們的選單場景已經成功設定好並且可以開始與使用者互動了。透過這種方式,場景管理器可以輕鬆處理每個場景的進入、退出以及事件處理,而且具有乾淨、高擴展性的應用程式架構。

處理場景中的事件

當進入選單場景時,我們會遇到不同類型的事件。這些事件主要包括自定義事件、返回事件、以及時間流逝(Tick)事件。我們通過事件處理函式來處理這些事件。

事件類型

事件類型被定義為 SceneManagerEventType,具有三個主要值:

  1. SceneManagerEventTypeCustom:這是自定義事件,用來表示像是使用者的互動或輸入等事件。
  2. SceneManagerEventTypeBack:這個事件表示使用者正試圖返回應用程式的上一個場景。如果沒有特別處理,場景管理器會自動將使用者帶回到上一個場景。
  3. SceneManagerEventTypeTick:這個事件表示時間流逝,場景應該刷新並有機會更新任何隨著時間推移而改變的數據模型。

處理自定義事件

當使用者在選單中選擇某個選項時,test_app_menu_callback_main_menu 會創建 SceneManagerEventTypeCustom 類型的事件,並通過 scene_manager_handle_custom_event 發送給場景管理器。這樣的事件會被傳遞到場景管理器的 _on_event 函式,進行處理。

這些事件包含來自 TestAppEvent 的值,例如 TestAppEvent_ShowPopupOneTestAppEvent_ShowPopupTwo。處理邏輯如下:

bool consumed = false;
switch(event.type) {
    case SceneManagerEventTypeCustom:
        switch(event.event) {
            case TestAppEvent_ShowPopupOne:
                scene_manager_next_scene(app->scene_manager, TestAppScene_FirstPopup);
                consumed = true;
                break;
            case TestAppEvent_ShowPopupTwo:
                scene_manager_next_scene(app->scene_manager, TestAppScene_SecondPopup);
                consumed = true;
                break;
        }
        break;
    default:
        consumed = false;
        break;
}
return consumed;

這段程式碼會根據不同的事件進行場景切換,例如當使用者選擇了第一個彈出視窗,則切換到 TestAppScene_FirstPopup 場景。

清理場景資源

當離開選單場景時,我們會調用 test_app_scene_on_exit_main_menu,用來清理場景的資源:

TestApp* app = context;
menu_reset(app->menu);

雖然在進入場景時我們也重設了選單,但在離開時再次清理資源是一個比較好的習慣,這樣可以確保場景中的資源不會長期佔用記憶體。

設定彈出視窗場景

與選單場景相比,彈出視窗場景的邏輯相對簡單。每個彈出視窗的主要邏輯集中在 *_on_enter 函式中,用來初始化視窗和設置其內容。例如,以下程式碼設置了一個彈出視窗:

popup_reset(app->popup);
popup_set_context(app->popup, app);
popup_set_header(app->popup, "Popup One", 64, 10, AlignCenter, AlignTop);
popup_set_icon(app->popup, 10, 10, &I_cvc_36x36);
popup_set_text(app->popup, "One! One popup. Ah ah ah...", 64, 20, AlignLeft, AlignTop);
view_dispatcher_switch_to_view(app->view_dispatcher, TestAppView_Popup);

在這裡,我們透過 popup_set_headerpopup_set_text 設置彈出視窗的標題和內容,最後使用 view_dispatcher_switch_to_view 切換到彈出視窗視圖。

清理彈出視窗

當離開彈出視窗場景時,我們會調用 popup_reset 來清理視窗的內容,確保不再佔用記憶體。

圖片資源管理

在程式碼中的幾個地方,我們可以看到以 I_ 為前綴的 Icon 指標。這些指標是由 ufbt 在編譯過程中自動從 images/ 資料夾中的資源創建出來的。

例如,在資料夾中有幾個圖像檔案:

  • cvc_36x36.png:這是一個 Count von Count 的小圖像,編譯後變為 I_cvc_36x46
  • one.png:10x10 尺寸的圖示,代表羅馬數字「i」,編譯後變為 I_one
  • two.png:10x10 尺寸的圖示,代表羅馬數字「ii」,編譯後變為 I_two

我們可以通過將更多的 1-bit PNG 檔案添加到該資料夾中,讓它們在編譯過程中被整合進應用程式,並在程式碼中以 Icon 資源的形式使用。

編譯與部署

首先,取得我們這邊用 instantiator.dev 在這篇部落格示範的專案示範:

git clone https://github.com/instantiator/flipper-zero-tutorial-app.git

接著,可以開始編譯應用程式:

$ ufbt
scons: Entering directory `/Users/lewiswestbury/.ufbt/current/scripts/ufbt'
        CC      /Users/lewiswestbury/src/personal/test_app/test_app.c
        CDB     /Users/lewiswestbury/src/personal/test_app/.vscode/compile_commands.json
        LINK    /Users/lewiswestbury/.ufbt/build/test_app_d.elf
        INSTALL /Users/lewiswestbury/src/personal/test_app/dist/debug/test_app_d.elf
        APPMETA /Users/lewiswestbury/.ufbt/build/test_app.fap
        FAP     /Users/lewiswestbury/.ufbt/build/test_app.fap
        INSTALL /Users/lewiswestbury/src/personal/test_app/dist/test_app.fap
        APPCHK  /Users/lewiswestbury/.ufbt/build/test_app.fap
                Target: 7, API: 26.0

如果我們的 ufbt 工具與 Flipper 設備上的韌體版本一致,就可以將它直接部署到 Flipper 上:

$ ufbt launch
scons: Entering directory `/Users/lewiswestbury/.ufbt/current/scripts/ufbt'
python3 "/Users/lewiswestbury/.ufbt/current/scripts/runfap.py" -s /Users/lewiswestbury/.ufbt/build/test_app.fap -t /ext/apps/Examples/test_app.fap
        APPCHK  /Users/lewiswestbury/.ufbt/build/test_app.fap
                Target: 7, API: 26.0
2023-05-06 23:38:36,824 [INFO] Using flip_Akurisau on /dev/cu.usbmodemflip_Akurisau1
2023-05-06 23:38:36,877 [INFO] Installing "/Users/lewiswestbury/.ufbt/build/test_app.fap" to /ext/apps/Examples/test_app.fap
2023-05-06 23:38:36,916 [INFO] Sending "/Users/lewiswestbury/.ufbt/build/test_app.fap" to "/ext/apps/Examples/test_app.fap"
100%, chunk 1 of 1
2023-05-06 23:38:37,108 [INFO] Launching app: "Applications" /ext/apps/Examples/test_app.fap

如果韌體版本與 ufbt 工具不一致,Flipper 會告訴我們。此時可以透過以下指令更新開發工具的 SDK:

ufbt update --channel=[dev|rc|release]

或者可以使用 qFlipper 來更新 Flipper 的韌體,或者使用以下指令進行 USB 刷入:

ufbt flash_usb

如果使用的是 ST-link,也可以使用以下指令:

ufbt flash

整合所有部分

通過本次的教學,我們已經介紹了如何為 Flipper Zero 應用程式初始化與啟動簡單的使用者介面。

作者提供完整的教學程式碼可以在文章最後的 References 找到,他鼓勵大家可以自由使用該專案的程式碼進行學習與修改。也建議各位去看看他的教學,並參考其他教學資源,進一步加深對 Flipper Zero 介面開發的了解。

按讚訂閱收藏小鈴噹叮叮叮

instantiator.dev 的教學我們已經完成了,明天我們會把健身海豚教練開發完成,並結束今年的鐵人賽挑戰!
各位期待明天最後一篇吧!

References


上一篇
Day28 - 會寫完喔!倒數 48 小時的勝利宣言,即將踏上終點的鐵人賽之旅!
下一篇
Day30 - 未完待續!撲朔迷離的十一月預告信?
系列文
Flipper Zero 宇宙最強攻略:30 天帶你從入門到入坑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言