Expose GitHub Sync credentials (username/PAT/repo/branch) via the web UI by moving them into CrossPointSettings, and fix Lockscreen incorrectly triggering on silent internal reboots (File Transfer/OPDS exit) instead of only on real wake/power-on
This commit is contained in:
@@ -10,11 +10,6 @@
|
|||||||
|
|
||||||
A plugin system for customizing and extending https://github.com/crosspoint-reader/crosspoint-reader firmware on your xteink device. Plugins are applied as source-level patches before the firmware is compiled and flashed.
|
A plugin system for customizing and extending https://github.com/crosspoint-reader/crosspoint-reader firmware on your xteink device. Plugins are applied as source-level patches before the firmware is compiled and flashed.
|
||||||
|
|
||||||
> **Note on this revision:** the plugin installer was rewritten from independent per-plugin `patch.py` scripts (which broke when installed as anything other than "all of them, in this exact order") to a shared declarative framework - see [Why plugin.py instead of patch.py](#why-pluginpy-instead-of-patchpy). Every plugin now installs correctly alone or in any combination. A few honest notes on functional gaps found and decisions made along the way:
|
|
||||||
> - **Smaller Fonts** actually only implements two states (Disabled/Enabled - one size step down) even though the UI text below describes three; the underlying code never distinguished a "Smallest" tier or handled OpenDyslexic. This rewrite matches what the code actually does rather than inventing the missing tier.
|
|
||||||
> - **Bookerly** generates 12/14/16/18pt only; the 8pt/10pt claim below wasn't actually wired up in the original code either. Bookerly also won't appear in the font picker on devices that also have SD-card custom fonts installed (a narrow pre-existing edge case).
|
|
||||||
> - **GitHub Sync**'s original boot hook only ran inside one rare boot path (quick-resume with no cached frame), so it silently never synced on most ordinary boots - it's now a proper once-per-boot hook. Its install-time NVS credential pre-seeding was dropped for simplicity; configure it via Settings → Plugins → GitHub Sync after flashing instead.
|
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
||||||
### Dark Mode
|
### Dark Mode
|
||||||
|
|||||||
@@ -1,41 +1,44 @@
|
|||||||
#include "GitHubSync.h"
|
#include "GitHubSync.h"
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <Preferences.h>
|
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "SDCardManager.h"
|
#include "SDCardManager.h"
|
||||||
|
|
||||||
#define GH_PREFS_NS "github_sync"
|
|
||||||
#define GH_KEY_USER "username"
|
|
||||||
#define GH_KEY_PAT "pat"
|
|
||||||
#define GH_KEY_REPO "repo"
|
|
||||||
#define GH_KEY_BRANCH "branch"
|
|
||||||
#define GH_SHA_DIR "/.crosspoint/github_sha/"
|
#define GH_SHA_DIR "/.crosspoint/github_sha/"
|
||||||
#define GH_BOOKS_DIR "/"
|
#define GH_BOOKS_DIR "/"
|
||||||
#define GH_SLEEP_BMP "sleep.bmp"
|
#define GH_SLEEP_BMP "sleep.bmp"
|
||||||
#define GH_SLEEP_PATH "/sleep.bmp"
|
#define GH_SLEEP_PATH "/sleep.bmp"
|
||||||
#define GH_API_BASE "https://api.github.com"
|
#define GH_API_BASE "https://api.github.com"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void setField(char* field, size_t fieldSize, const std::string& value) {
|
||||||
|
strncpy(field, value.c_str(), fieldSize - 1);
|
||||||
|
field[fieldSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Config now lives in CrossPointSettings (persisted via SETTINGS.saveToFile()/
|
||||||
|
// loadFromFile(), same as every other plugin setting) instead of a private NVS
|
||||||
|
// namespace, so it's reachable through the standard settings list - which is
|
||||||
|
// what makes it show up in the web UI's Settings page alongside Dark Mode,
|
||||||
|
// Smaller Fonts, etc. On-device editing via GitHubSyncSettingsActivity is
|
||||||
|
// unaffected since it only goes through this struct-based API.
|
||||||
bool GitHubSync::loadConfig(GitHubSyncConfig &cfg) {
|
bool GitHubSync::loadConfig(GitHubSyncConfig &cfg) {
|
||||||
Preferences prefs;
|
cfg.username = SETTINGS.githubUsername;
|
||||||
prefs.begin(GH_PREFS_NS, true);
|
cfg.pat = SETTINGS.githubPat;
|
||||||
cfg.username = prefs.getString(GH_KEY_USER, "").c_str();
|
cfg.repo = SETTINGS.githubRepo[0] ? SETTINGS.githubRepo : "xteink";
|
||||||
cfg.pat = prefs.getString(GH_KEY_PAT, "").c_str();
|
cfg.branch = SETTINGS.githubBranch[0] ? SETTINGS.githubBranch : "main";
|
||||||
cfg.repo = prefs.getString(GH_KEY_REPO, "xteink").c_str();
|
|
||||||
cfg.branch = prefs.getString(GH_KEY_BRANCH, "main").c_str();
|
|
||||||
prefs.end();
|
|
||||||
return !cfg.username.empty() && !cfg.pat.empty();
|
return !cfg.username.empty() && !cfg.pat.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GitHubSync::saveConfig(const GitHubSyncConfig &cfg) {
|
void GitHubSync::saveConfig(const GitHubSyncConfig &cfg) {
|
||||||
Preferences prefs;
|
setField(SETTINGS.githubUsername, sizeof(SETTINGS.githubUsername), cfg.username);
|
||||||
prefs.begin(GH_PREFS_NS, false);
|
setField(SETTINGS.githubPat, sizeof(SETTINGS.githubPat), cfg.pat);
|
||||||
prefs.putString(GH_KEY_USER, cfg.username.c_str());
|
setField(SETTINGS.githubRepo, sizeof(SETTINGS.githubRepo), cfg.repo);
|
||||||
prefs.putString(GH_KEY_PAT, cfg.pat.c_str());
|
setField(SETTINGS.githubBranch, sizeof(SETTINGS.githubBranch), cfg.branch);
|
||||||
prefs.putString(GH_KEY_REPO, cfg.repo.c_str());
|
SETTINGS.saveToFile();
|
||||||
prefs.putString(GH_KEY_BRANCH, cfg.branch.c_str());
|
|
||||||
prefs.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitHubSync::isConfigured() {
|
bool GitHubSync::isConfigured() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import os
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
from framework.manifest import (
|
from framework.manifest import (
|
||||||
PluginManifest, SourceFile, Include, SettingActionEnumValue, PluginsTabEntry, MainHook,
|
PluginManifest, SourceFile, Include, SettingsField, SettingActionEnumValue, PluginsTabEntry, MainHook,
|
||||||
)
|
)
|
||||||
|
|
||||||
# NOTE on scope: the original patch.py also pre-provisioned GitHub credentials
|
# NOTE on scope: the original patch.py also pre-provisioned GitHub credentials
|
||||||
@@ -11,10 +11,16 @@ from framework.manifest import (
|
|||||||
# generated nvs.csv + `esptool ... write-flash 0x3000`), so users wouldn't
|
# generated nvs.csv + `esptool ... write-flash 0x3000`), so users wouldn't
|
||||||
# have to type a PAT on-device. That's flash-time, not source-patch-time
|
# have to type a PAT on-device. That's flash-time, not source-patch-time
|
||||||
# logic, and GitHubSyncSettingsActivity already lets you configure
|
# logic, and GitHubSyncSettingsActivity already lets you configure
|
||||||
# username/PAT/repo/branch on-device (or you can add web-UI editing later),
|
# username/PAT/repo/branch on-device, so this rewrite drops the NVS
|
||||||
# so this rewrite drops the NVS pre-seeding step for simplicity/safety and
|
# pre-seeding step for simplicity/safety and has you configure GitHub Sync
|
||||||
# has you configure GitHub Sync via Settings -> Plugins -> GitHub Sync after
|
# via Settings -> Plugins -> GitHub Sync after first boot instead. Ask if you
|
||||||
# first boot instead. Ask if you want the NVS pre-seeding restored.
|
# want the NVS pre-seeding restored.
|
||||||
|
#
|
||||||
|
# Config now lives in CrossPointSettings (char fields below) rather than a
|
||||||
|
# private NVS namespace, so it also shows up as editable text fields in the
|
||||||
|
# web UI's Settings -> Plugins section, same mechanism as Hardcover's API
|
||||||
|
# token - GitHubSync.cpp's loadConfig()/saveConfig() read/write these fields
|
||||||
|
# directly instead of the old Preferences-based store.
|
||||||
#
|
#
|
||||||
# Also fixed in this rewrite: the original main.cpp hook only ran
|
# Also fixed in this rewrite: the original main.cpp hook only ran
|
||||||
# GitHubSync::sync() inside one narrow boot branch (quick-resume-with-no-
|
# GitHubSync::sync() inside one narrow boot branch (quick-resume-with-no-
|
||||||
@@ -38,6 +44,12 @@ def get_manifest(ctx):
|
|||||||
Include("GitHubSyncSettingsActivity.h", "settings_activity_cpp"),
|
Include("GitHubSyncSettingsActivity.h", "settings_activity_cpp"),
|
||||||
Include("activities/settings/GitHubSync.h", "main_cpp"),
|
Include("activities/settings/GitHubSync.h", "main_cpp"),
|
||||||
],
|
],
|
||||||
|
settings_fields=[
|
||||||
|
SettingsField('char githubUsername[64] = "";'),
|
||||||
|
SettingsField('char githubPat[256] = "";'),
|
||||||
|
SettingsField('char githubRepo[64] = "xteink";'),
|
||||||
|
SettingsField('char githubBranch[32] = "main";'),
|
||||||
|
],
|
||||||
setting_actions=[
|
setting_actions=[
|
||||||
SettingActionEnumValue("GitHubSync"),
|
SettingActionEnumValue("GitHubSync"),
|
||||||
],
|
],
|
||||||
@@ -49,6 +61,35 @@ def get_manifest(ctx):
|
|||||||
action_value_text="Sync",
|
action_value_text="Sync",
|
||||||
activity_launch_expr="std::make_unique<GitHubSyncSettingsActivity>(renderer, mappedInput)",
|
activity_launch_expr="std::make_unique<GitHubSyncSettingsActivity>(renderer, mappedInput)",
|
||||||
),
|
),
|
||||||
|
# show_on_device=False: the device already edits these through the
|
||||||
|
# GitHub Sync activity above, so a second on-device row would just
|
||||||
|
# be redundant. hidden_from_web is left at its default (False) so
|
||||||
|
# they show up as text fields in the web UI's Plugins section.
|
||||||
|
PluginsTabEntry(
|
||||||
|
label="GitHub Username",
|
||||||
|
kind="string",
|
||||||
|
key="githubUsername",
|
||||||
|
show_on_device=False,
|
||||||
|
),
|
||||||
|
PluginsTabEntry(
|
||||||
|
label="GitHub Personal Access Token",
|
||||||
|
kind="string",
|
||||||
|
key="githubPat",
|
||||||
|
obfuscated=True,
|
||||||
|
show_on_device=False,
|
||||||
|
),
|
||||||
|
PluginsTabEntry(
|
||||||
|
label="GitHub Repo",
|
||||||
|
kind="string",
|
||||||
|
key="githubRepo",
|
||||||
|
show_on_device=False,
|
||||||
|
),
|
||||||
|
PluginsTabEntry(
|
||||||
|
label="GitHub Branch",
|
||||||
|
kind="string",
|
||||||
|
key="githubBranch",
|
||||||
|
show_on_device=False,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
main_hooks=[
|
main_hooks=[
|
||||||
MainHook(
|
MainHook(
|
||||||
|
|||||||
@@ -73,7 +73,12 @@ def get_manifest(ctx):
|
|||||||
" {\n"
|
" {\n"
|
||||||
" const auto lsMode = static_cast<LockscreenMode>(SETTINGS.lockscreenMode);\n"
|
" const auto lsMode = static_cast<LockscreenMode>(SETTINGS.lockscreenMode);\n"
|
||||||
" const bool hasPinSet = SETTINGS.lockscreenPinHash[0] != '\\0';\n"
|
" const bool hasPinSet = SETTINGS.lockscreenPinHash[0] != '\\0';\n"
|
||||||
" const bool shouldLock = hasPinSet && LockscreenPlugin::shouldLock(lsMode);\n"
|
" // BootResume::Silent covers internal silentRestart() cycles used to\n"
|
||||||
|
" // reset WiFi/webserver state (e.g. leaving File Transfer or the OPDS\n"
|
||||||
|
" // browser) - the user never actually slept, so don't lock for those.\n"
|
||||||
|
" // QuickResume (waking from sleep) and Splash (real power-on) still lock.\n"
|
||||||
|
" const bool shouldLock = hasPinSet && resume != BootResume::Silent &&\n"
|
||||||
|
" LockscreenPlugin::shouldLock(lsMode);\n"
|
||||||
" if (shouldLock) {\n"
|
" if (shouldLock) {\n"
|
||||||
" LockscreenActivity lockAct(renderer, mappedInputManager, LockscreenActivity::Purpose::UNLOCK);\n"
|
" LockscreenActivity lockAct(renderer, mappedInputManager, LockscreenActivity::Purpose::UNLOCK);\n"
|
||||||
" lockAct.onEnter();\n"
|
" lockAct.onEnter();\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user