diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index a948ea5a8..af66e074c 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -294,3 +294,7 @@ STR_LINK: "[link]" STR_SCREENSHOT_BUTTON: "Take screenshot" STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)" +STR_CRASH_TITLE: "System Crash" +STR_CRASH_DESCRIPTION: "A detailed report was saved to crash_report.txt. Please include this file in your bug report." +STR_CRASH_REASON: "Crash reason:" +STR_CRASH_NO_REASON: "(No reason was recorded)" diff --git a/src/activities/ActivityManager.cpp b/src/activities/ActivityManager.cpp index 3cf115b13..bd15abbaf 100644 --- a/src/activities/ActivityManager.cpp +++ b/src/activities/ActivityManager.cpp @@ -5,6 +5,7 @@ #include "boot_sleep/BootActivity.h" #include "boot_sleep/SleepActivity.h" #include "browser/OpdsBookBrowserActivity.h" +#include "home/CrashActivity.h" #include "home/FileBrowserActivity.h" #include "home/HomeActivity.h" #include "home/RecentBooksActivity.h" @@ -196,6 +197,8 @@ void ActivityManager::goToFullScreenMessage(std::string message, EpdFontFamily:: replaceActivity(std::make_unique(renderer, mappedInput, std::move(message), style)); } +void ActivityManager::goToCrashReport() { replaceActivity(std::make_unique(renderer, mappedInput)); } + void ActivityManager::goHome() { replaceActivity(std::make_unique(renderer, mappedInput)); } void ActivityManager::pushActivity(std::unique_ptr&& activity) { diff --git a/src/activities/ActivityManager.h b/src/activities/ActivityManager.h index bc24c42e3..bc975e919 100644 --- a/src/activities/ActivityManager.h +++ b/src/activities/ActivityManager.h @@ -86,6 +86,7 @@ class ActivityManager { void goToSleep(); void goToBoot(); void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR); + void goToCrashReport(); void goHome(); // This will move current activity to stack instead of deleting it diff --git a/src/activities/home/CrashActivity.cpp b/src/activities/home/CrashActivity.cpp new file mode 100644 index 000000000..4f5a392b9 --- /dev/null +++ b/src/activities/home/CrashActivity.cpp @@ -0,0 +1,61 @@ +#include "CrashActivity.h" + +#include +#include +#include + +#include "components/UITheme.h" +#include "fontIds.h" + +void CrashActivity::onEnter() { + Activity::onEnter(); + + panicMessage = HalSystem::getPanicInfo(false); + if (panicMessage.empty()) { + panicMessage = tr(STR_CRASH_NO_REASON); + } + HalSystem::clearPanic(); + + requestUpdateAndWait(); +} + +void CrashActivity::loop() { + if (mappedInput.isPressed(MappedInputManager::Button::Back)) { + finish(); + } +} + +void CrashActivity::render(RenderLock&&) { + renderer.clearScreen(); + + const auto& metrics = UITheme::getInstance().getMetrics(); + const auto pageWidth = renderer.getScreenWidth(); + const auto contentWidth = pageWidth - 2 * metrics.contentSidePadding; + const auto x = metrics.contentSidePadding; + const auto lineHeight = renderer.getLineHeight(UI_10_FONT_ID); + + GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_CRASH_TITLE)); + + int y = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + + auto descLines = renderer.wrappedText(UI_10_FONT_ID, tr(STR_CRASH_DESCRIPTION), contentWidth, 10); + for (const auto& line : descLines) { + renderer.drawText(UI_10_FONT_ID, x, y, line.c_str()); + y += lineHeight; + } + + y += metrics.verticalSpacing * 2; + renderer.drawText(UI_10_FONT_ID, x, y, tr(STR_CRASH_REASON)); + y += lineHeight + metrics.verticalSpacing; + + auto panicLines = renderer.wrappedText(UI_10_FONT_ID, panicMessage.c_str(), contentWidth, 5); + for (const auto& line : panicLines) { + renderer.drawText(UI_10_FONT_ID, x, y, line.c_str()); + y += lineHeight; + } + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} diff --git a/src/activities/home/CrashActivity.h b/src/activities/home/CrashActivity.h new file mode 100644 index 000000000..9b4641ed1 --- /dev/null +++ b/src/activities/home/CrashActivity.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Activity.h" + +class CrashActivity final : public Activity { + std::string panicMessage; + + public: + explicit CrashActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) + : Activity("Crash", renderer, mappedInput) {} + void onEnter() override; + void loop() override; + void render(RenderLock&&) override; +}; diff --git a/src/main.cpp b/src/main.cpp index e77e8ba6f..e51a24333 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,7 +253,6 @@ void setup() { } HalSystem::checkPanic(); - HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info SETTINGS.loadFromFile(); I18N.loadSettings(); @@ -290,10 +289,13 @@ void setup() { APP_STATE.loadFromFile(); RECENT_BOOKS.loadFromFile(); - // Boot to home screen if no book is open, last sleep was not from reader, back button is held, or reader activity - // crashed (indicated by readerActivityLoadCount > 0) - if (APP_STATE.openEpubPath.empty() || !APP_STATE.lastSleepFromReader || - mappedInputManager.isPressed(MappedInputManager::Button::Back) || APP_STATE.readerActivityLoadCount > 0) { + if (HalSystem::isRebootFromPanic()) { + // If we rebooted from a panic, go to crash report screen to show the panic info + activityManager.goToCrashReport(); + } else if (APP_STATE.openEpubPath.empty() || !APP_STATE.lastSleepFromReader || + mappedInputManager.isPressed(MappedInputManager::Button::Back) || APP_STATE.readerActivityLoadCount > 0) { + // Boot to home screen if no book is open, last sleep was not from reader, back button is held, or reader activity + // crashed (indicated by readerActivityLoadCount > 0) activityManager.goHome(); } else { // Clear app state to avoid getting into a boot loop if the epub doesn't load