feat: Current page as QR (#1099)

## Summary

* **What is the goal of this PR?** Implements QR text of the current
page
* **What changes are included?**

## Additional Context

I saw this feature request at #982 
It made sense to me so I implemented. But if the team thinks it is not
necessary please let me know and we can close the PR.

| Page | Menu | QR |
|------|-------|----|
|
![IMG_6601.bmp](https://github.com/user-attachments/files/25473201/IMG_6601.bmp)
|
![IMG_6599.bmp](https://github.com/user-attachments/files/25473202/IMG_6599.bmp)
|
![IMG_6600.bmp](https://github.com/user-attachments/files/25473205/IMG_6600.bmp)
|


---

### AI Usage


Did you use AI tools to help write this code? _** YES

---------

Co-authored-by: Eliz Kilic <elizk@google.com>
This commit is contained in:
Eliz
2026-02-25 09:12:31 +00:00
committed by GitHub
parent 31396da064
commit 128eb614a6
10 changed files with 199 additions and 34 deletions

View File

@@ -1,6 +1,7 @@
#include "EpubReaderActivity.h"
#include <Epub/Page.h>
#include <Epub/blocks/TextBlock.h>
#include <FsHelpers.h>
#include <GfxRenderer.h>
#include <HalStorage.h>
@@ -14,6 +15,7 @@
#include "KOReaderCredentialStore.h"
#include "KOReaderSyncActivity.h"
#include "MappedInputManager.h"
#include "QrDisplayActivity.h"
#include "RecentBooksStore.h"
#include "components/UITheme.h"
#include "fontIds.h"
@@ -403,6 +405,38 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
}));
break;
}
case EpubReaderMenuActivity::MenuAction::DISPLAY_QR: {
if (section && section->currentPage >= 0 && section->currentPage < section->pageCount) {
auto p = section->loadPageFromSectionFile();
if (p) {
std::string fullText;
for (const auto& el : p->elements) {
if (el->getTag() == TAG_PageLine) {
const auto& line = static_cast<const PageLine&>(*el);
if (line.getBlock()) {
const auto& words = line.getBlock()->getWords();
for (const auto& w : words) {
if (!fullText.empty()) fullText += " ";
fullText += w;
}
}
}
}
if (!fullText.empty()) {
exitActivity();
enterNewActivity(new QrDisplayActivity(renderer, mappedInput, fullText, [this]() {
exitActivity();
requestUpdate();
}));
break;
}
}
}
// If no text or page loading failed, just close menu
exitActivity();
requestUpdate();
break;
}
case EpubReaderMenuActivity::MenuAction::GO_HOME: {
// Defer go home to avoid race condition with display task
pendingGoHome = true;

View File

@@ -12,7 +12,16 @@
class EpubReaderMenuActivity final : public ActivityWithSubactivity {
public:
// Menu actions available from the reader menu.
enum class MenuAction { SELECT_CHAPTER, GO_TO_PERCENT, ROTATE_SCREEN, SCREENSHOT, GO_HOME, SYNC, DELETE_CACHE };
enum class MenuAction {
SELECT_CHAPTER,
GO_TO_PERCENT,
ROTATE_SCREEN,
SCREENSHOT,
DISPLAY_QR,
GO_HOME,
SYNC,
DELETE_CACHE
};
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
const int currentPage, const int totalPages, const int bookProgressPercent,
@@ -39,11 +48,14 @@ class EpubReaderMenuActivity final : public ActivityWithSubactivity {
};
// Fixed menu layout (order matters for up/down navigation).
const std::vector<MenuItem> menuItems = {
{MenuAction::SELECT_CHAPTER, StrId::STR_SELECT_CHAPTER}, {MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION},
{MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT}, {MenuAction::SCREENSHOT, StrId::STR_SCREENSHOT_BUTTON},
{MenuAction::GO_HOME, StrId::STR_GO_HOME_BUTTON}, {MenuAction::SYNC, StrId::STR_SYNC_PROGRESS},
{MenuAction::DELETE_CACHE, StrId::STR_DELETE_CACHE}};
const std::vector<MenuItem> menuItems = {{MenuAction::SELECT_CHAPTER, StrId::STR_SELECT_CHAPTER},
{MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION},
{MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT},
{MenuAction::SCREENSHOT, StrId::STR_SCREENSHOT_BUTTON},
{MenuAction::DISPLAY_QR, StrId::STR_DISPLAY_QR},
{MenuAction::GO_HOME, StrId::STR_GO_HOME_BUTTON},
{MenuAction::SYNC, StrId::STR_SYNC_PROGRESS},
{MenuAction::DELETE_CACHE, StrId::STR_DELETE_CACHE}};
int selectedIndex = 0;
ButtonNavigator buttonNavigator;

View File

@@ -0,0 +1,45 @@
#include "QrDisplayActivity.h"
#include <GfxRenderer.h>
#include <I18n.h>
#include "MappedInputManager.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "util/QrUtils.h"
void QrDisplayActivity::onEnter() {
Activity::onEnter();
requestUpdate();
}
void QrDisplayActivity::onExit() { Activity::onExit(); }
void QrDisplayActivity::loop() {
if (mappedInput.wasReleased(MappedInputManager::Button::Back) ||
mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
onGoBack();
return;
}
}
void QrDisplayActivity::render(Activity::RenderLock&&) {
renderer.clearScreen();
auto metrics = UITheme::getInstance().getMetrics();
const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight();
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_DISPLAY_QR), nullptr);
const int availableWidth = pageWidth - 40;
const int availableHeight = pageHeight - metrics.topPadding - metrics.headerHeight - metrics.verticalSpacing * 2 - 40;
const int startY = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const Rect qrBounds(20, startY, availableWidth, availableHeight);
QrUtils::drawQrCode(renderer, qrBounds, textPayload);
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <I18n.h>
#include <string>
#include "activities/Activity.h"
class QrDisplayActivity final : public Activity {
public:
explicit QrDisplayActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& textPayload,
const std::function<void()>& onGoBack)
: Activity("QrDisplay", renderer, mappedInput), textPayload(textPayload), onGoBack(onGoBack) {}
void onEnter() override;
void onExit() override;
void loop() override;
void render(Activity::RenderLock&&) override;
private:
std::string textPayload;
const std::function<void()> onGoBack;
};