Skip to content

JohnChourp/LearnByzantineMusic

Repository files navigation

LearnByzantineMusic

Overview

Το LearnByzantineMusic είναι Android εφαρμογή εκμάθησης Βυζαντινής Μουσικής με ενότητες θεωρίας. Η λειτουργία Συνθέτης έχει καταργηθεί και έχει αντικατασταθεί από σελίδα 8 Ήχοι με οπτική απόδοση κλιμάκων και διαστημάτων. Στη σελίδα 8 Ήχοι εμφανίζονται επίσης ανά ήχο: δεσπόζοντες φθόγγοι, φθορές, έλξεις και κατηγορίες καταλήξεων (ατελείς/εντελείς/τελικές/οριστικαί). Στη σελίδα 8 Ήχοι εμφανίζονται ανά ήχο και τα απηχήματα (λεκτικό απήχημα + φθογγόσημο βάσης από τα διαθέσιμα drawables του app). Στη σελίδα 8 Ήχοι εμφανίζονται επίσης οι φθόγγοι εκτέλεσης κάθε απηχήματος (έναρξη → κίνηση → κατάληξη) για καθοδήγηση στην εκφώνηση. Στη σελίδα 8 Ήχοι εμφανίζεται επίσης αντιστοίχιση συλλαβή → φθόγγος για κάθε απήχημα, ώστε η εκφώνηση να γίνεται σωστά βήμα-βήμα. Στη σελίδα 8 Ήχοι εμφανίζονται επίσης εναλλακτικές ονομασίες απηχημάτων όπου υπάρχουν στην πράξη (π.χ. Πλ. Β’). Όπου υπάρχει εναλλακτικό απήχημα, εμφανίζονται και οι δικοί του φθόγγοι καθώς και η αντιστοίχιση συλλαβών. Στη σελίδα 8 Ήχοι εμφανίζεται πλέον και σταθερή ένδειξη ότι κάθε απήχημα εκτελείται σε δύο μορφές: σύντομο και αργό. Η οπτική κλίμακα των 8 Ήχων έχει επεκταθεί σε τριπλό εύρος (Νη, → Νη΄΄) με 22 φθόγγους και 21 διαστήματα ανά ήχο. Για τον Α΄ Ήχο υπάρχει πλέον ξεχωριστή σελίδα Θεωρία Α΄ Ήχου, η οποία ανοίγει από CTA μέσα στη σελίδα 8 Ήχοι και συγκεντρώνει οργανωμένα το αναλυτικό θεωρητικό υλικό με verified τοπικά σύμβολα του app. Προστέθηκε σελίδα Ρυθμίσεις με discrete slider για μέγεθος γραμμάτων (20/40/60/80/100) που εφαρμόζει global font scaling σε όλες τις οθόνες. Προστέθηκε δίγλωσση υποστήριξη (Ελληνικά / English) με υποχρεωτικό πρώτο wizard επιλογής γλώσσας και επιβεβαίωση πριν την ενεργοποίηση. Στη σελίδα Ρυθμίσεις προστέθηκαν δύο κουμπιά γλώσσας (Ελληνικά, English) που αλλάζουν locale σε όλη την εφαρμογή μετά από επιβεβαίωση. Προστέθηκε πλήρως ανασχεδιασμένη σελίδα Ηχογραφήσεις (Compose UI) με μεγάλη εγγραφή μικροφώνου, παύση/συνέχεια, σταμάτημα και λίστα μόνο με τις 10 τελευταίες ηχογραφήσεις που δημιουργήθηκαν από το app (τοπικό history στη συσκευή). Η σελίδα Ηχογραφήσεις ζητά υποχρεωτικά επιλογή φακέλου μέσω SAF στην πρώτη είσοδο και αποθηκεύει μόνιμη πρόσβαση. Οι ηχογραφήσεις μπορούν να αποθηκευτούν σε .flac/.mp3/.wav/.aac/.m4a/.opus με default το .flac και περιγραφή για κάθε format μέσα στο UI. Προστέθηκε ξεχωριστή σελίδα Διαχείριση ηχογραφήσεων (Compose UI) για πλοήγηση φακέλων, δημιουργία φακέλου, μετακίνηση μέσω action menu Move to... (χωρίς drag/drop), και inline rename/delete. Προστέθηκε νέα σελίδα Σημειώσεις (Compose UI) με πολλαπλές σημειώσεις (τίτλος + κείμενο), local-first αποθήκευση σε Room, auto-save όσο γράφει ο χρήστης και χειροκίνητο Save τώρα. Η σελίδα Σημειώσεις απαιτεί υποχρεωτικά επιλογή φακέλου backup/sync μέσω SAF στην πρώτη είσοδο, δημιουργεί full JSON snapshot σε κάθε save και κρατά απεριόριστο ιστορικό snapshots. Υποστηρίζεται Εξαγωγή τώρα, Import backup (replace-all) και pending resync ροή όταν το write στον εξωτερικό φάκελο αποτυγχάνει (χωρίς να χάνεται το τοπικό save). Η λίστα Ηχογραφήσεις εμφανίζει σταθερά τις 10 τελευταίες δικές μου ηχογραφήσεις, ενώ η Διαχείριση ηχογραφήσεων διατηρεί ταξινόμηση/αναζήτηση/φίλτρα και virtualization/paging πάνω από Room index για απόκριση σε μεγάλους φακέλους. Διορθώθηκε η κενή κατάσταση της σελίδας Ηχογραφήσεις ώστε όταν δεν υπάρχουν δικές μου εγγραφές να εμφανίζεται άμεσα μήνυμα δεν υπάρχουν εγγραφές αντί για ατέρμονο loading. Η σελίδα Σάρωση Βυζαντινού Κειμένου έχει τεθεί προσωρινά ανενεργή και δεν είναι διαθέσιμη από την αρχική πλοήγηση. Προστέθηκε σελίδα Ημερολόγιο που εμφανίζει τον ήχο εβδομάδας ανά επιλεγμένη ημερομηνία. Η σελίδα Ημερολόγιο υποστηρίζει πλέον άμεση επιλογή μήνα/έτους με tap στον τίτλο μήνα (picker), κουμπί Σήμερα και πιο ευανάγνωστο UI με οπτική διάκριση επιλεγμένης ημέρας και σημερινής ημέρας. Προστέθηκε πιλοτικό εορτολόγιο ημέρας (Ιανουάριος 2025) με local dataset (calendar_celebrations_v1.json), dot ένδειξη ειδικής ημέρας στο grid και αναλυτικό panel κάτω από τον ήχο εβδομάδας. Στο Ημερολόγιο εμφανίζονται πλέον μόνο παραπομπές αναγνωσμάτων (Απόστολος/Ευαγγέλιο), και με tap σε παραπομπή ανοίγει νέα σελίδα πλήρους κειμένου με εναλλαγή Αρχαία / Νεοελληνικά. Το dataset ημερολογίου επεκτάθηκε με section readings (reference, text_ancient, text_modern) χωρίς πεδία source/domain/url. Προστέθηκε script scripts/fill_calendar_month.py για monthly update (YYYY-MM ή DD-MM-YYYY) με internet retrieval pipeline (year page -> day ids -> full readings page parse). Οι protected ημερομηνίες (αμετακίνητες γιορτές + επίσημες αργίες) στο days πλέον δεν γίνονται overwrite όταν υπάρχουν ήδη και συμπληρώνονται μόνο αν λείπουν. Προστέθηκε script scripts/seed_protected_days.py για deterministic seed protected ημερών χωρίς internet retrieval (π.χ. 2025-2026). Για τον Ιανουάριο 2025 τα text_ancient/text_modern των αναγνωσμάτων αντικαταστάθηκαν από πραγματικά πλήρη κείμενα (με υποστήριξη πολλαπλών αναγνωσμάτων ανά ημέρα όπου υπάρχουν). Το skill/script εφαρμόζει πολιτική provider-1 (Κολιτσάρα) -> provider-2 (Τρεμπέλα) -> provider-3 (license-gated) -> generated fallback και δεν αποθηκεύει/εμφανίζει πληροφορία πηγής στο app. Ο υπολογισμός ήχου γίνεται από εκκλησιαστικό κύκλο: Ορθόδοξο Πάσχα -> Πεντηκοστή (+49) -> αρχή Α’ Ήχου στη 2η Κυριακή μετά την Πεντηκοστή. Η νέα ροή αποκόπτει το πρώτο μουσικό block/γραμμή, εμφανίζει το cropped αποτέλεσμα και προβάλλει confidence ανά σύμβολο. Η ανάλυση παράγει πορεία φθόγγων (Νη/Πα/Βου/Γα/Δι/Κε/Ζω) με βάση τον επιλεγμένο ήχο και τη βάση εκκίνησης. Προστέθηκε pipeline δημιουργίας template dataset από MK/fonts + KeyBoard.ini στο app/src/main/assets/mk_symbol_templates_v1. Στο Core MVP v2 ο scanner engine χρησιμοποιεί primary OCR templates από core drawables και semantic parser base+modifier (π.χ. πεταστή, απόστροφος, κλάσμα, γοργό, αντικένωμα+απλή). Ο επιλεγμένος Ήχος επηρεάζει πλέον πραγματικά την καμπύλη πορείας μέσω mode profiles (byzantine_mode_rules_v1.json), ενώ η διάρκεια ανά event αποδίδεται με κανόνες χρόνου. Πλέον υποστηρίζεται και αυτοματοποιημένη διαδικασία release στο GitHub με tag-based publish, user-friendly release notes και ένα custom release asset (apk-release.apk). Το build classpath κάνει forced resolve transitive εξαρτήσεις ασφαλείας: commons-io σε 2.14.0, protobuf-java σε 3.25.5, jdom2 σε 2.0.6.1, netty-codec σε 4.1.129.Final, netty-codec-http σε 4.1.129.Final, netty-codec-http2 σε 4.1.129.Final, netty-handler σε 4.1.129.Final, jose4j σε 0.9.6, commons-compress σε 1.26.0, commons-lang3 σε 3.18.0, bcpkix-jdk18on σε 1.79, bcprov-jdk18on σε 1.79 και bcutil-jdk18on σε 1.79. Για το app dependency graph υπάρχει πλέον και explicit pin στο com.google.guava:guava:32.1.3-jre (catalog + implementation + kapt) ώστε το security graph να αναγνωρίζει deterministic patched version.

Business flow

  • Ο χρήστης ανοίγει την αρχική οθόνη και επιλέγει θεωρητική ενότητα.
  • Στο κάτω μέρος της αρχικής οθόνης εμφανίζεται footer με μορφή poweredby JohnChourp v.<release_version>.
  • Το footer με το poweredby JohnChourp v.<release_version> είναι σταθερό στο κάτω μέρος της αρχικής σελίδας (εκτός scroll περιοχής).
  • Από την αρχική οθόνη ο χρήστης μπορεί να ανοίξει τη σελίδα Ρυθμίσεις.
  • Στη σελίδα Ρυθμίσεις ο χρήστης αλλάζει το μέγεθος γραμμάτων με slider που κουμπώνει μόνο στις τιμές 20/40/60/80/100 (προεπιλογή 60).
  • Η αλλαγή αποθηκεύεται άμεσα σε local preferences (app_font_step) και εφαρμόζεται global σε όλες τις activities.
  • Στην πρώτη εκκίνηση εμφανίζεται υποχρεωτικό menu επιλογής γλώσσας (Ελληνικά / English) και ακολουθεί επιβεβαίωση στη γλώσσα που επιλέχθηκε.
  • Με επιβεβαίωση επιλογής, το app αποθηκεύει app_language_code, μαρκάρει ολοκλήρωση onboarding (app_language_onboarding_completed) και επανεκκινεί στην ίδια γλώσσα.
  • Στη σελίδα Ρυθμίσεις, τα δύο κουμπιά γλώσσας (Ελληνικά, English) ζητούν πάντα επιβεβαίωση πριν από κάθε αλλαγή locale.
  • Πατώντας Ηχογραφήσεις, ανοίγει σελίδα εγγραφής με controls (Έναρξη/Παύση/Συνέχεια/Σταμάτημα) και λίστα με τις 10 τελευταίες ηχογραφήσεις που δημιουργήθηκαν από το app.
  • Στην πρώτη είσοδο της σελίδας Ηχογραφήσεις, αν δεν υπάρχει αποθηκευμένη πρόσβαση φακέλου, ανοίγει picker φακέλου (OpenDocumentTree) και απαιτείται παραχώρηση άδειας.
  • Πατώντας Σημειώσεις, ανοίγει νέα σελίδα για πολλαπλές σημειώσεις (τίτλος + κείμενο) με αναζήτηση και ταξινόμηση κατά πρόσφατη ενημέρωση.
  • Στην πρώτη είσοδο της σελίδας Σημειώσεις, αν δεν υπάρχει αποθηκευμένη πρόσβαση φακέλου backup, ανοίγει υποχρεωτικά picker φακέλου (OpenDocumentTree) και επανεμφανίζεται μέχρι να επιλεγεί έγκυρος φάκελος.
  • Σε κάθε save σημείωσης (auto-save ή Save τώρα) η εφαρμογή γράφει πρώτα local δεδομένα στη Room και έπειτα δημιουργεί full JSON snapshot για sync στον επιλεγμένο φάκελο.
  • Αν το sync φακέλου αποτύχει, το save παραμένει επιτυχές τοπικά και το snapshot μπαίνει σε pending queue για επόμενο retry ή Επανασυγχρονισμό.
  • Με Import backup γίνεται replace-all επαναφορά των σημειώσεων, και αμέσως μετά δημιουργείται νέο snapshot/sync.
  • Με Έναρξη ηχογράφησης το μεγάλο κόκκινο κουμπί κρύβεται και εμφανίζονται Παύση/Συνέχεια + Σταμάτημα.
  • Με Σταμάτημα το app αποθηκεύει την εγγραφή στο επιλεγμένο format (FLAC default) και ενημερώνει άμεσα το τοπικό history recent.
  • Με Παραχώρηση/Αλλαγή φακέλου εμφανίζεται πρώτα επιβεβαίωση (Συνέχεια/Ακύρωση) ώστε να μη χαθεί ο τρέχων φάκελος από κατά λάθος πάτημα.
  • Με Άνοιγμα φακέλου γίνεται προσπάθεια ανοίγματος ακριβώς του επιλεγμένου SAF φακέλου (και fallback σε picker με preselected αρχικό φάκελο).
  • Με tap σε στοιχείο ηχογράφησης (τόσο στη recent λίστα όσο και στη Διαχείριση ηχογραφήσεων) γίνεται ασφαλές προσωρινό cache copy και ανοίγει chooser Android (Άνοιγμα ηχογράφησης με...) για επιλογή εφαρμογής αναπαραγωγής με μεγαλύτερη συμβατότητα.
  • Το tap σε κάθε row ηχογράφησης έχει πλέον εμφανές pressed effect (ripple) και status Άνοιγμα ηχογράφησης: ... ώστε να είναι ξεκάθαρο ότι καταγράφηκε το πάτημα.
  • Η recent λίστα τροφοδοτείται από local history που κρατά μόνο ηχογραφήσεις που έγιναν από το app και προβάλλει τις 10 νεότερες (createdTs DESC).
  • Η κύρια σελίδα Ηχογραφήσεις δεν εκτελεί full SAF reindex στο άνοιγμα (ούτε στην πρώτη εκκίνηση), ώστε το empty state/πρόσφατες εγγραφές να εμφανίζονται άμεσα.
  • Όταν η recent λίστα είναι άδεια, εμφανίζεται deterministic empty state και δεν προβάλλεται ατέρμονο loading indicator.
  • Η recent λίστα δεν έχει πλέον search/sort/filter controls, ενώ τα controls παραμένουν στη λίστα διαχείρισης.
  • Στη Διαχείριση ηχογραφήσεων οι φάκελοι εμφανίζονται πάντα πρώτοι και μετά ακολουθούν τα αρχεία ήχου.
  • Και στις δύο λίστες (recent + διαχείριση) εμφανίζονται μόνο αρχεία ήχου με κατάληξη .flac/.mp3/.wav/.aac/.m4a/.opus.
  • Κάθε στοιχείο ηχογράφησης έχει διακριτικά κουμπιά Μετονομασία και κόκκινο Διαγραφή με επιβεβαίωση πριν την ενέργεια.
  • Η φόρτωση/ανανέωση λιστών ηχογραφήσεων γίνεται μέσω Room indexer (BFS σάρωση SAF), WorkManager one-time reindex jobs και Paging3 queries ώστε το UI να παραμένει responsive σε μεγάλους φακέλους.
  • Με Διαχείριση ηχογραφήσεων ανοίγει ξεχωριστή σελίδα όπου εμφανίζονται φάκελοι + audio files του τρέχοντος path, επιτρέπεται δημιουργία φακέλου, μετακίνηση μέσω Move to... dialog (searchable target folders) και inline rename/delete.
  • Αν ένα αρχείο έχει διαγραφεί/μετακινηθεί εκτός app, σε Άνοιγμα/Μετονομασία/Διαγραφή εμφανίζεται μήνυμα καταργήθηκε (localized) και αφαιρείται από τη λίστα.
  • Πατώντας 8 Ήχοι, ανοίγει η οθόνη επιλογής ήχου.
  • Πατώντας Ημερολόγιο, ανοίγει νέα οθόνη μηνιαίου ημερολογίου.
  • Στη σελίδα Ημερολόγιο, η τρέχουσα ημέρα της συσκευής επιλέγεται αυτόματα κατά την είσοδο.
  • Στη σελίδα Ημερολόγιο, με tap στον τίτλο μήνα ανοίγει picker για γρήγορη επιλογή μήνα/έτους (1900-2100) χωρίς διαδοχικά πατήματα στα βελάκια.
  • Στη σελίδα Ημερολόγιο, το κουμπί Σήμερα επιστρέφει άμεσα στον τρέχοντα μήνα/ημερομηνία.
  • Πατώντας οποιαδήποτε ημέρα, εμφανίζεται ο ήχος εβδομάδας για την εβδομάδα της συγκεκριμένης ημερομηνίας.
  • Πατώντας ημέρα στο Ημερολόγιο, εμφανίζεται επίσης το Εορτολόγιο ημέρας (κύρια εγγραφή + επιπλέον εγγραφές αν υπάρχουν).
  • Πατώντας ημέρα στο Ημερολόγιο, εμφανίζονται και παραπομπές Απόστολος/Ευαγγέλιο (χωρίς URLs/πηγές).
  • Με tap σε παραπομπή ανοίγει η σελίδα Κείμενο αναγνώσματος, όπου ο χρήστης εναλλάσσει Αρχαία και Νεοελληνικά για το ίδιο ανάγνωσμα.
  • Στο μηνιαίο grid του Ημερολογίου, εμφανίζεται dot indicator μόνο όταν η ημέρα έχει ειδική καταχώρηση (αργία/θρησκευτική εορτή) και όχι για normal_day.
  • Ο ήχος εβδομάδας αλλάζει στο app την Κυριακή 00:00 τοπικής ώρας.
  • Η Σάρωση Βυζαντινού Κειμένου παραμένει προσωρινά ανενεργή ακόμα και με άμεσο intent άνοιγμα.
  • Πριν το Ανάλυση, ο χρήστης μπορεί να κάνει περικοπή της φωτογραφίας (αριστερά/δεξιά/πάνω/κάτω) για να αφαιρέσει περιττές πληροφορίες.
  • Η περικοπή ενημερώνεται live όσο μετακινούνται οι γραμμές (sliders), ώστε να φαίνεται άμεσα τι μένει στο τελικό κάδρο.
  • Προστέθηκε και περιστροφή εικόνας (οριζόντια/κάθετα/στραβά) με slider γωνίας και πλήκτρα -90° / +90°.
  • Η λήψη φωτογραφίας γίνεται πλέον με in-app κάμερα (portrait lock), ώστε να μην εξαρτάται από auto-rotate εξωτερικής εφαρμογής κάμερας.
  • Με Ανάλυση, το app κάνει local adaptive preprocessing (threshold+morphology+deskew), αποκόπτει το πρώτο μουσικό block και αναγνωρίζει σύμβολα με parser base+modifier.
  • Κάτω από κάθε αναγνωρισμένο σύμβολο εμφανίζεται αυτόματα ο αντίστοιχος φθόγγος.
  • Για κάθε event εμφανίζονται και διάρκεια (χρόνος) και confidence.
  • Τα άγνωστα σύμβολα επισημαίνονται ως UNKNOWN και η ανάλυση συνεχίζει χωρίς διακοπή.
  • Η πορεία μελωδίας εμφανίζεται και ως ακολουθία κειμένου και ως mode-aware γραφική τροχιά σε ξεχωριστό διάγραμμα.
  • Προεπιλεγμένος είναι ο Α’ Ήχος.
  • Με αλλαγή ήχου από το selector, ανανεώνονται γένος, αναλυτικά θεωρητικά στοιχεία του ήχου, φθόγγοι ανόδου, διαστήματα (μόρια) και το διάγραμμα «σκάλα».
  • Με αλλαγή ήχου από το selector, ανανεώνονται και το απήχημα του ήχου και το αντίστοιχο φθογγόσημο βάσης.
  • Με αλλαγή ήχου από το selector, ανανεώνονται και οι φθόγγοι εκτέλεσης του απηχήματος ώστε να φαίνεται καθαρά από πού ξεκινά, πού ανεβαίνει και πού καταλήγει.
  • Με αλλαγή ήχου από το selector, ανανεώνεται και η αντιστοίχιση συλλαβών/φθόγγων του απηχήματος για σωστή προφορά κάθε συλλαβής.
  • Με αλλαγή ήχου από το selector, εμφανίζονται και εναλλακτικά απηχήματα (όταν υπάρχουν) ώστε να καλύπτεται και η εναλλακτική ορολογία.
  • Στα εναλλακτικά απηχήματα (όπου υπάρχουν) εμφανίζονται ξεχωριστά οι φθόγγοι και η αντιστοίχιση συλλαβών/φθόγγων.
  • Στο selector των ήχων, το όνομα χρωματίζεται ανά γένος: μαύρο για διατονικούς, μπλε για σκληρό χρωματικό, μωβ για μαλακό χρωματικό και πορτοκαλί για εναρμόνιο.
  • Το ύψος κάθε οπτικού διαστήματος παραμένει αναλογικό στα μόρια.
  • Η κλίμακα του διαγράμματος καλύπτει πλέον 3 οκτάβες (Νη, έως Νη΄΄) με παραδοσιακή σήμανση χαμηλής/υψηλής οκτάβας.
  • Πάνω από το διάγραμμα υπάρχει πλέον κάρτα Μεταφορά βάσης με slider (-12 έως +12 μόρια) και κουμπί Επαναφορά.
  • Η μεταφορά βάσης αλλάζει μόνο το απόλυτο ύψος (transpose) και κρατάει ίδια διαστήματα/ονομασίες φθόγγων.
  • Κάθε ήχος θυμάται τη δική του τιμή μεταφοράς βάσης και η τιμή αποθηκεύεται μόνιμα στη συσκευή.
  • Με press-and-hold πάνω στο όνομα φθόγγου στο διάγραμμα, αναπαράγεται συνεχής τόνος στη συχνότητα του συγκεκριμένου φθόγγου για τον επιλεγμένο ήχο.
  • Το touch playback καλύπτει όλο το τριπλό εύρος, από χαμηλό Νη, μέχρι υψηλό Νη΄΄.
  • Με απελευθέρωση (UP) ή έξοδο του δαχτύλου εκτός label (EXIT), ο τόνος σταματά άμεσα.
  • Για νέα έκδοση app, ο maintainer τρέχει scripts/release-and-tag.sh (ή το skill wrapper), γίνεται bump έκδοσης, build release artifacts, ενιαίο commit με όλες τις αλλαγές του working tree, και tag push.
  • Το release script δημιουργεί αυτόματα συνοπτική, user-friendly περιγραφή αλλαγών από previous tag σε νέο tag (RELEASE_NOTES.md) με πλήρη λίστα commits, χωρίς να επαναλαμβάνει τον τίτλο του release.
  • Το release script δημοσιεύει άμεσα GitHub Release με μόνο custom asset το apk-release.apk (για εύκολο mobile install download) και χρησιμοποιεί τα generated notes ως release description.
  • Τα Source code (zip) και Source code (tar.gz) εμφανίζονται αυτόματα από το GitHub σε κάθε tag release.
  • Το release script και το GitHub Action δημοσιεύουν μόνο signed APK· αν λείπουν signing credentials, το release μπλοκάρεται πριν το upload.
  • Πριν από release γίνεται αυτόματος έλεγχος ότι δεν υπάρχουν committed secrets/keystore αρχεία στο repository.
  • Σε κάθε push/pull request τρέχει αυτόματα ο έλεγχος Security Guard για ανίχνευση committed secrets.
  • Με push tag vX.Y.Z, το GitHub Actions workflow παραμένει ως επιπλέον fallback για release packaging και ανεβάζει μόνο alias apk-release.apk.
  • Αν στο ίδιο tag υπάρχει ήδη custom asset apk-release.apk από direct publish του script, το fallback workflow κάνει skip το publish για να μη δημιουργηθεί δεύτερο APK asset.

Κύριες αμετάβλητες αρχές:

  • Η σελίδα 8 Ήχοι είναι εκπαιδευτική προβολή με τοπικό interaction ακρόασης φθόγγων και αποθήκευση τοπικής μεταφοράς βάσης ανά ήχο.
  • Δεν υπάρχει backend API/cloud.
  • Κάθε release πρέπει να έχει μοναδικό versionCode και semantic versionName.

Inputs & outputs

Input (UI επιλογή ήχου)

{
  "selected_mode": "Α’ Ήχος"
}

Output (δεδομένα προβολής)

{
  "mode": "Α’ Ήχος",
  "genus": "Διατονικό",
  "apichima": "Ανανές",
  "apichima_alternatives": "",
  "apichima_phthongs": "Πα → Βου → Πα",
  "apichima_syllables_phthongs": "Α(Πα) - να(Βου) - νές(Πα)",
  "mode_theory": {
    "dominant_phthongs": ["Πα", "Κε"],
    "phthores": "Κυρίως διατονική, χρωματική σε μεταβάσεις",
    "elxeis": ["Βου→Γα", "Ζω→Νη"],
    "cadences_atelis": ["Γα", "Κε"],
    "cadences_entelis": ["Πα", "Νη"],
    "cadences_telikes": ["Πα"],
    "cadences_oristikes": ["Πα"]
  },
  "ascending_phthongs": ["Νη,", "Πα,", "Βου,", "Γα,", "Δι,", "Κε,", "Ζω,", "Νη", "Πα", "Βου", "Γα", "Δι", "Κε", "Ζω", "Νη΄", "Πα΄", "Βου΄", "Γα΄", "Δι΄", "Κε΄", "Ζω΄", "Νη΄΄"],
  "ascending_moria": [12, 10, 8, 12, 12, 10, 8, 12, 10, 8, 12, 12, 10, 8, 12, 10, 8, 12, 12, 10, 8]
}

Input (επιλογή ημερομηνίας στο ημερολόγιο)

{
  "selected_date": "2026-02-08"
}

Output (ήχος εβδομάδας ημερολογίου)

{
  "week_start": "2026-02-08",
  "week_end": "2026-02-14",
  "tone_index": 4,
  "tone_name": "Πλάγιος του Α’"
}

Input (ηχογράφηση από μικρόφωνο)

{
  "folder_uri": "content://com.android.externalstorage.documents/tree/primary%3AMusic%2FByzantineRecordings",
  "format": "flac",
  "action": "start_pause_resume_stop"
}

Output (αποθήκευση ηχογράφησης)

{
  "saved_file_name": "recording_20260220_104500.flac",
  "folder": "ByzantineRecordings",
  "format": "flac",
  "status": "saved"
}

Input (save σημείωσης)

{
  "note_id": "4f5eb74f-6362-4bb6-a618-0f37f8bf9c39",
  "title": "Χερουβικό Κυριακής",
  "body": "Να δουλέψω αργά το ανέβασμα στο «Και ζωοποιώ».",
  "trigger": "auto_or_manual"
}

Output (notes snapshot sync)

{
  "local_saved": true,
  "snapshot_schema_version": 1,
  "snapshot_file": "notes_snapshot_20260306_213015_144.json",
  "folder_sync": "success_or_pending_retry",
  "pending_sync_count": 0
}

Input (touch φθόγγου στο διάγραμμα)

{
  "mode": "Α’ Ήχος",
  "touch_action": "DOWN",
  "phthong_label": "Πα",
  "index_top_to_bottom": 13
}

Output (ήχος φθόγγου)

{
  "base_frequency_hz": 220.0,
  "moria_from_base_ni": 12,
  "formula": "f = 220 * 2^(moria/72)",
  "frequency_hz": 246.94,
  "playback": "continuous_while_pressed"
}

Input (ρύθμιση μεγέθους γραμμάτων)

{
  "settings_page": true,
  "font_size_step": 80
}

Output (global font scale)

{
  "stored_key": "app_font_step",
  "stored_value": 80,
  "font_scale": 1.1,
  "scope": "all_activities"
}

Input (επιλογή γλώσσας)

{
  "first_launch": true,
  "selected_language": "en",
  "confirm": true
}

Output (global locale)

{
  "stored_key_language": "app_language_code",
  "stored_value_language": "en",
  "stored_key_onboarding": "app_language_onboarding_completed",
  "stored_value_onboarding": true,
  "scope": "all_activities"
}

Input (release automation)

{
  "command": "./scripts/release-and-tag.sh --bump patch"
}

Input (camera scan analysis)

{
  "mode": "Πλάγιος του Β’",
  "base_phthong": "Νη",
  "image_source": "camera_or_gallery",
  "analysis_scope": "first_music_block"
}

Output (camera scan analysis)

{
  "crop_rect": {"left": 42, "top": 130, "right": 1170, "bottom": 260},
  "events_count": 8,
  "unknown_count": 2,
  "low_confidence_count": 1,
  "events": [
    {"name": "Πεταστή", "token": "a4", "modifiers": [], "confidence": 0.91, "note": "Βου", "duration_beats": 1.0},
    {"name": "Απόστροφος", "token": "b1", "modifiers": ["gorgo"], "confidence": 0.72, "note": "Νη", "duration_beats": 0.5}
  ],
  "note_path": ["Βου", "Πα", "Πα", "Βου", "Γα", "Βου", "Πα", "Νη"]
}

Output (release automation)

{
  "version_name": "1.0.3",
  "version_code": 3,
  "tag": "v1.0.3",
  "github_release": "published",
  "release_notes": "build-artifacts/release/v1.0.3/RELEASE_NOTES.md",
  "assets": ["apk-release.apk", "source-code-zip", "source-code-tar-gz"]
}

Configuration

  • Android build:

  • compileSdk = 34

  • minSdk = 24

  • targetSdk = 34

  • Compose Compiler Extension = 1.5.14

  • Kotlin Gradle Plugin = 1.9.24

  • AGP = 8.5.2

  • Buildscript classpath override:

  • commons-io:commons-io = 2.14.0 (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • com.google.protobuf:protobuf-java = 3.25.5 (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • org.jdom:jdom2 = 2.0.6.1 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • io.netty:netty-codec = 4.1.129.Final (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • io.netty:netty-codec-http = 4.1.129.Final (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • io.netty:netty-codec-http2 = 4.1.129.Final (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • io.netty:netty-handler = 4.1.129.Final (forced μέσω root build.gradle.kts για transitive hardening από AGP/UTP)

  • org.bitbucket.b_c:jose4j = 0.9.6 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • org.apache.commons:commons-compress = 1.26.0 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • org.apache.commons:commons-lang3 = 3.18.0 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • org.bouncycastle:bcpkix-jdk18on = 1.79 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • org.bouncycastle:bcprov-jdk18on = 1.79 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • org.bouncycastle:bcutil-jdk18on = 1.79 (forced μέσω root build.gradle.kts για transitive hardening από AGP)

  • com.google.guava:guava = 32.1.3-jre (explicit pin στο app dependency graph για mitigation του temporary-directory advisory)

  • com.arthenica:ffmpeg-kit-full-gpl = 6.0-2 (για transcode ηχογραφήσεων σε flac/mp3/aac/m4a/opus)

  • Κύρια components:

  • MainActivity

  • WeeklyModeCalendarActivity

  • SettingsActivity

  • EightModesActivity

  • BaseActivity

  • AppFontScale

  • LiturgicalToneCycle

  • OrthodoxPaschaCalculator

  • layout_eight_modes.xml

  • layout_weekly_mode_calendar.xml

  • layout_settings.xml

  • ScaleDiagramView

  • PhthongTonePlayer

  • ByzantineScanActivity

  • ByzantineMelodyAnalyzer

  • MelodyPathView

  • RecordingsActivity

  • RecordingsManagerActivity

  • RecordingFormatOption

  • RecordingsPrefs

  • AudioTranscoder

  • RecordingDocumentOps

  • RecordingModels

  • NotesActivity

  • NotesViewModel

  • NotesRepository

  • NotesDatabase

  • NotesBackupManager

  • NotesBackupCodec

  • Ρυθμίσεις εφαρμογής:

  • SharedPreferences file: learn_byzantine_music_settings

  • Key: app_font_step

  • Allowed values: 20 | 40 | 60 | 80 | 100

  • Default value: 60

  • SharedPreferences file: learn_byzantine_music_recordings

  • Keys: recordings_folder_tree_uri, recordings_output_format

  • Default format: FLAC

  • SharedPreferences file: learn_byzantine_music_notes

  • Keys: notes_folder_tree_uri, notes_last_sync_epoch_ms, notes_last_sync_error

  • Room DB: notes.db (NoteEntity: id, title, body, createdAtEpochMs, updatedAtEpochMs)

  • Internal pending backup directory: files/notes_pending_snapshots/

  • Release automation scripts:

  • scripts/bump-version.sh

  • scripts/release-and-tag.sh

  • scripts/generate-mk-symbol-dataset.py

  • scripts/check-no-secrets.sh

  • scripts/setup-release-signing.sh

  • .codex/AGENTS.md (project-specific Codex safety instructions)

  • GitHub Actions:

  • .github/workflows/android-release.yml (trigger σε tags v*.*.*)

  • .github/workflows/security-guard.yml (trigger σε κάθε push/pull request)

  • .github/workflows/codeql.yml (CodeQL scan με manual Java/Kotlin build μέσω :app:compileDebugKotlin και JDK 17)

  • Υποχρεωτικό release signing για installable GitHub APK:

  • ANDROID_KEYSTORE_BASE64

  • ANDROID_KEYSTORE_PASSWORD

  • ANDROID_KEY_ALIAS

  • ANDROID_KEY_PASSWORD

  • Για local release script απαιτούνται επίσης env vars:

  • ANDROID_SIGNING_STORE_FILE

  • ANDROID_SIGNING_STORE_PASSWORD

  • ANDROID_SIGNING_KEY_ALIAS

  • ANDROID_SIGNING_KEY_PASSWORD

  • Required GitHub permissions:

  • contents: write για δημιουργία release και upload assets.

  • Κανόνας ασφάλειας:

  • Δεν γίνεται commit σε .env*, *.jks, *.keystore, *.p12, *.pfx, *.pem, key.properties.

  • Ευαίσθητες τιμές περνάνε μόνο από GitHub Secrets και runtime env vars.

  • Δεν απαιτούνται:

  • login/auth provider

  • push/SNS υποδομές

  • backend resources

Examples

Happy path (UI)

  • Ο χρήστης ανοίγει την αρχική οθόνη και βλέπει στο κάτω μέρος poweredby JohnChourp v.1.0.3.
  • Πατά Ρυθμίσεις, μετακινεί το slider στο 80 και η εφαρμογή εμφανίζει άμεσα μεγαλύτερα γράμματα.
  • Έπειτα ανοίγει 8 Ήχοι, επιλέγει Πλάγιος του Β’ και βλέπει άμεσα ενημερωμένη κλίμακα/διαστήματα με σωστή οπτική αναλογία.
  • Κρατά πατημένο τον χαμηλό Νη, και έπειτα τον υψηλό Νη΄΄, και ακούει σωστή διαφορά ύψους σε όλο το τριπλό εύρος.
  • Τέλος ανοίγει Σάρωση Βυζαντινού Κειμένου, τραβά φωτογραφία, και βλέπει cropped block + πορεία φθόγγων ανά σύμβολο.

Happy path (notes)

  • Ο χρήστης ανοίγει Σημειώσεις, επιλέγει φάκελο backup και δημιουργεί νέα σημείωση.
  • Καθώς γράφει, ενεργοποιείται auto-save και δημιουργούνται JSON snapshots στον εξωτερικό φάκελο.
  • Πατά Import backup με παλιό snapshot, η εφαρμογή κάνει replace-all στη λίστα και γράφει άμεσα νέο snapshot.

Happy path (release)

./scripts/setup-release-signing.sh --set-github-secrets
source "$HOME/.android/learnbyzantine/release-signing.env"
./scripts/release-and-tag.sh --bump patch
  • Αναμενόμενο: νέο commit έκδοσης, νέο tag vX.Y.Z, push στο GitHub, direct publish GitHub Release και upload μόνο apk-release.apk.
  • Το commit του release δημιουργείται αυτόματα ως ένα ενιαίο commit που περιλαμβάνει όλες τις διαθέσιμες αλλαγές (tracked/untracked, εκτός ignored) με σύντομο summary στο commit message.
  • Το GitHub Release description περιλαμβάνει συνοπτική εικόνα (commits/files/contributors), περιοχές που επηρεάστηκαν και πλήρη λίστα αλλαγών από το προηγούμενο release, χωρίς διπλό τίτλο.
  • Το fallback workflow για το ίδιο tag εντοπίζει αν υπάρχει ήδη apk-release.apk και τότε παραλείπει το publish step (αναμενόμενη συμπεριφορά).

Failure example

{
  "error": "tag_already_exists",
  "message": "Το tag v1.0.3 υπάρχει ήδη. Δώσε νέο version bump ή explicit version."
}

Edge-case example

{
  "error": "missing_signing_secrets",
  "result": "release_aborted_before_publish",
  "action": "ορισμός ANDROID_KEYSTORE_* secrets στο GitHub και ANDROID_SIGNING_* vars στο local release environment"
}

Failure example (secrets guard)

{
  "error": "tracked_secret_detected",
  "message": "Βρέθηκε committed secret/keystore αρχείο. Μεταφορά σε GitHub Secrets και αφαίρεση από git history/index."
}

Failure example (camera permission denied)

{
  "error": "camera_permission_denied",
  "fallback": "gallery_available",
  "message": "Η κάμερα απορρίφθηκε. Χρησιμοποίησε gallery ή άνοιξε άδεια από ρυθμίσεις."
}

Failure example (notes sync failed but local saved)

{
  "error": "notes_backup_folder_write_failed",
  "local_saved": true,
  "pending_sync": true,
  "message": "Το περιεχόμενο σώθηκε τοπικά και θα συγχρονιστεί στο επόμενο save/resync."
}

Συχνές ερωτήσεις (FAQ)

Γιατί εμφανίστηκε Dependabot alert για commons-io;

  • Το commons-io δεν υπάρχει ως direct dependency στο app module.
  • Έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2) και σχετικά UTP artifacts.
  • Το project κάνει forced resolve σε commons-io:2.14.0 στο build classpath ώστε να καλύπτεται το patched range του advisory.

Γιατί εμφανίστηκε Dependabot alert για protobuf-java;

  • Το protobuf-java έρχεται transitive από AGP/UTP dependencies και όχι από direct declaration στο app module.
  • Η προηγούμενη resolved έκδοση ήταν 3.22.3 και το advisory ζητά patched έκδοση >= 3.25.5.
  • Το project κάνει forced resolve σε com.google.protobuf:protobuf-java:3.25.5 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για jdom2;

  • Το jdom2 έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2).
  • Η προηγούμενη resolved έκδοση ήταν 2.0.6 και το advisory ζητά patched έκδοση >= 2.0.6.1.
  • Το project κάνει forced resolve σε org.jdom:jdom2:2.0.6.1 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για netty-codec;

  • Το io.netty:netty-codec έρχεται transitive από AGP/UTP dependencies.
  • Η προηγούμενη resolved έκδοση ήταν 4.1.93.Final και το advisory ζητά patched έκδοση >= 4.1.125.Final.
  • Το project κάνει forced resolve σε io.netty:netty-codec:4.1.129.Final στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για netty-codec-http;

  • Το io.netty:netty-codec-http έρχεται transitive από AGP/UTP dependencies.
  • Η προηγούμενη resolved έκδοση ήταν 4.1.93.Final και το advisory ζητά patched έκδοση >= 4.1.129.Final.
  • Το project κάνει forced resolve σε io.netty:netty-codec-http:4.1.129.Final στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για netty-codec-http2;

  • Το io.netty:netty-codec-http2 έρχεται transitive από AGP/UTP dependencies.
  • Η προηγούμενη resolved έκδοση ήταν 4.1.93.Final και τα advisories καλύπτονται από patched έκδοση 4.1.129.Final.
  • Το project κάνει forced resolve σε io.netty:netty-codec-http2:4.1.129.Final, που ανεβάζει και τα σχετικά Netty modules στο ίδιο resolved graph.

Γιατί εμφανίστηκε Dependabot alert για netty-handler;

  • Το io.netty:netty-handler έρχεται transitive από AGP/UTP dependencies.
  • Το advisory καλύπτει εύρος 4.1.91.Final έως 4.1.117.Final και απαιτεί >= 4.1.118.Final.
  • Το project κάνει explicit forced resolve σε io.netty:netty-handler:4.1.129.Final στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για jose4j;

  • Το org.bitbucket.b_c:jose4j έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2).
  • Η προηγούμενη resolved έκδοση ήταν 0.9.5 και το advisory ζητά patched έκδοση >= 0.9.6.
  • Το project κάνει forced resolve σε org.bitbucket.b_c:jose4j:0.9.6 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για commons-compress;

  • Το org.apache.commons:commons-compress έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2).
  • Η προηγούμενη resolved έκδοση ήταν 1.21 και το advisory ζητά patched έκδοση >= 1.26.0.
  • Το project κάνει forced resolve σε org.apache.commons:commons-compress:1.26.0 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για commons-lang3;

  • Το org.apache.commons:commons-lang3 έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2).
  • Η προηγούμενη resolved έκδοση ήταν 3.14.0 και το advisory ζητά patched έκδοση >= 3.18.0.
  • Το project κάνει forced resolve σε org.apache.commons:commons-lang3:3.18.0 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για bcpkix-jdk18on;

  • Το org.bouncycastle:bcpkix-jdk18on έρχεται transitive από το Android Gradle Plugin (com.android.tools.build:gradle:8.5.2).
  • Η προηγούμενη resolved έκδοση ήταν 1.77 και το advisory ζητά patched έκδοση >= 1.79.
  • Το project κάνει forced resolve σε org.bouncycastle:bcpkix-jdk18on:1.79 στο build classpath και ευθυγραμμίζει bcprov/bcutil στην ίδια έκδοση.

Γιατί εμφανίστηκε Dependabot alert για bcprov-jdk18on;

  • Το org.bouncycastle:bcprov-jdk18on έρχεται transitive από AGP dependencies (bcpkix-jdk18on/bcutil-jdk18on).
  • Η προηγούμενη resolved έκδοση ήταν 1.77 και τα advisories καλύπτονται με έκδοση 1.79.
  • Το project κάνει forced resolve σε org.bouncycastle:bcprov-jdk18on:1.79 στο build classpath.

Γιατί εμφανίστηκε Dependabot alert για guava;

  • Το com.google.guava:guava έρχεται transitive από androidx.room και androidx.work dependencies.
  • Το advisory για insecure use of temporary directory καλύπτεται από patched γραμμή >= 32.0.0-android, με σύσταση απο maintainers να αποφεύγεται το 32.0.0.
  • Το project κάνει explicit pin σε com.google.guava:guava:32.1.3-jre στο app dependency graph (implementation + kapt) και κρατά force fallback για πλήρη ευθυγράμμιση resolve.

Γιατί αποτυγχάνει το login/auth;

  • Η εφαρμογή δεν χρησιμοποιεί login/auth ροή.
  • Δεν απαιτούνται token/session ή identity provider.

Δεν έρχονται notifications. Τι να ελέγξω;

  • Δεν υπάρχει μηχανισμός push/SNS στο app.
  • Δεν υπάρχουν backend notification routes.

Το dispatch/routes δεν ενημερώνεται σωστά. Τι να ελέγξω;

  • Δεν υπάρχει dispatch/routes ροή στην εφαρμογή.
  • Η λογική είναι αποκλειστικά τοπική προβολή περιεχομένου.

Γιατί δεν μπορώ να ξεκινήσω ηχογράφηση;

  • Συνήθης αιτία είναι ότι δεν έχει δοθεί ακόμα πρόσβαση σε φάκελο μέσω SAF (OpenDocumentTree).
  • Βεβαιώσου ότι έχει δοθεί και άδεια μικροφώνου (RECORD_AUDIO) στο app.
  • Αν ακυρώθηκε ο picker φακέλου, πάτησε Παραχώρηση/Αλλαγή φακέλου και επέλεξε ξανά φάκελο.

Δεν αποθηκεύεται στο format που διάλεξα. Τι να ελέγξω;

  • Έλεγξε ότι στο selector της σελίδας Ηχογραφήσεις έχει επιλεγεί το σωστό format πριν την έναρξη.
  • Για μη-WAV formats η μετατροπή γίνεται μέσω FFmpeg και απαιτεί επιτυχή transcode στο τέλος της εγγραφής.
  • Αν αποτύχει transcode, εμφανίζεται μήνυμα σφάλματος και δεν δημιουργείται τελικό αρχείο στον φάκελο.

Δεν ανοίγει ο φάκελος σε file explorer. Τι να ελέγξω;

  • Χρειάζεται διαθέσιμη εφαρμογή που να υποστηρίζει ACTION_VIEW για directory URI.
  • Αν δεν υπάρχει συμβατή εφαρμογή, το app εμφανίζει μήνυμα και η εγγραφή συνεχίζει να λειτουργεί κανονικά.
  • Εγκατάστησε/ενεργοποίησε file manager app και ξαναδοκίμασε το Άνοιγμα φακέλου.

Πάτησα κατά λάθος «Παραχώρηση/Αλλαγή φακέλου». Χάνεται ο τρέχων φάκελος;

  • Όχι. Πλέον εμφανίζεται πρώτα επιβεβαίωση με Συνέχεια/Ακύρωση.
  • Αν πατήσεις Ακύρωση ή κλείσεις τον picker χωρίς επιλογή, ο προηγούμενος φάκελος παραμένει ενεργός.
  • Η λίστα δείχνει μόνο τις 10 τελευταίες ηχογραφήσεις που έγιναν από το app στο ίδιο SAF root.

Γιατί στις Σημειώσεις μου λέει ότι το sync απέτυχε ενώ η αποθήκευση ολοκληρώθηκε;

  • Η λογική είναι local-first: πρώτα αποθηκεύει πάντα τοπικά (Room), μετά γράφει backup στον εξωτερικό φάκελο.
  • Αν αποτύχει το write στον φάκελο (provider/permission σφάλμα), το snapshot μένει σε pending queue στο app και γίνεται retry στο επόμενο save.
  • Δεν χάνονται οι σημειώσεις σου από αυτή την αποτυχία, εκτός αν χαλάσει η τοπική βάση και δεν υπάρχει κανένα έγκυρο backup.

Μπορώ να χάσω σημειώσεις αν διαγράψω την εφαρμογή;

  • Αν έχεις επιλέξει φάκελο backup και έχουν παραχθεί snapshots, μπορείς να κάνεις Import backup μετά από reinstall.
  • Αν δεν είχε επιλεγεί ποτέ φάκελος backup ή δεν υπάρχουν snapshots, η απεγκατάσταση αφαιρεί το τοπικό Room DB.
  • Για μέγιστη ασφάλεια, κράτα τον εξωτερικό φάκελο backup σε σημείο που δεν διαγράφεται μαζί με την εφαρμογή.

Τι ακριβώς κάνει το Import backup;

  • Διαβάζει ένα JSON snapshot και κάνει replace-all στις σημειώσεις (δεν κάνει merge).
  • Μετά το replace-all, η εφαρμογή δημιουργεί νέο snapshot και προσπαθεί άμεσο sync στον επιλεγμένο φάκελο.
  • Αν το import αρχείο είναι άκυρο (schemaVersion/δομή), η επαναφορά απορρίπτεται χωρίς να πειραχτούν οι υπάρχουσες σημειώσεις.

Πώς ανοίγω μία ηχογράφηση από τη λίστα;

  • Πάτησε το αρχείο στη recent λίστα.
  • Το app δημιουργεί προσωρινό αντίγραφο στο cache και το ανοίγει μέσω FileProvider για καλύτερη συμβατότητα με εξωτερικούς players.
  • Εμφανίζεται chooser με τις συμβατές εφαρμογές αναπαραγωγής, ακόμη κι όταν ο SAF provider του αρχικού αρχείου έχει περιορισμούς.
  • Αν δεν υπάρχει συμβατή εφαρμογή, εμφανίζεται το μήνυμα αποτυχίας ανοίγματος.

Αργεί να ανοίξει η λίστα όταν έχω πολλά αρχεία ηχογραφήσεων. Τι να ελέγξω;

  • Η κύρια σελίδα Ηχογραφήσεις (τελευταίες 10 δικές μου εγγραφές) φορτώνει από local history και πρέπει να ανοίγει άμεσα, ακόμη και στην πρώτη εκκίνηση χωρίς εγγραφές.
  • Το βαρύ indexing/reindex αφορά τη σελίδα Διαχείριση ηχογραφήσεων (Room index + Paging3), όχι τη recent λίστα της κύριας σελίδας.
  • Αν υπάρχει καθυστέρηση στη Διαχείριση ηχογραφήσεων μετά από πολλές αλλαγές αρχείων, περίμενε να ολοκληρωθεί το reindex (linear progress στην κορυφή).
  • Αν ο SAF provider της συσκευής καθυστερεί σε πολύ βαθύ tree, δοκίμασε πιο «ρηχή» δομή φακέλων και ξανάνοιξε τη σελίδα για νέο warm-cache load.

Γιατί απέτυχε μετακίνηση στη «Διαχείριση ηχογραφήσεων»;

  • Η μετακίνηση γίνεται από action menu (Move to...) και βασίζεται σε SAF moveDocument, οπότε ορισμένοι providers μπορεί να μην το υποστηρίζουν για όλα τα paths.
  • Αν source και target φάκελος είναι ίδιος, η εφαρμογή μπλοκάρει προληπτικά τη μετακίνηση.
  • Η εφαρμογή μπλοκάρει επίσης μετακίνηση φακέλου στον εαυτό του ή σε υποφάκελό του για αποφυγή άκυρων κύκλων.

Δεν ακούγεται ο τόνος στους φθόγγους. Τι να ελέγξω;

  • Επιβεβαίωσε ότι γίνεται press-and-hold πάνω στο ίδιο το label του φθόγγου (όχι στο κενό του διαγράμματος).
  • Έλεγξε ένταση media του device και ότι δεν είναι σε muted/silent mode.
  • Αν άλλαξες ήχο από selector την ώρα που έπαιζε τόνος, ξαναπάτησε hold σε label για νέο playback.
  • Το playback λειτουργεί σε όλο το εύρος Νη, → Νη΄΄, άρα δοκίμασε τόσο χαμηλά όσο και ψηλά labels για έλεγχο εξόδου ήχου.

Πώς δουλεύει η «Μεταφορά βάσης» στους 8 ήχους;

  • Η μπάρα αλλάζει τη βάση από -12 έως +12 μόρια και κάνει transpose σε όλη την κλίμακα, χωρίς να αλλάζει ονόματα φθόγγων ή διαστήματα.
  • Κάθε ήχος κρατάει δική του τιμή βάσης και η τιμή αποθηκεύεται μόνιμα (παραμένει μετά από κλείσιμο/άνοιγμα app).
  • Η σελίδα θυμάται και τον τελευταίο επιλεγμένο ήχο από το πάνω selector και τον επαναφέρει αυτόματα στο επόμενο άνοιγμα.
  • Με Επαναφορά η τρέχουσα τιμή του συγκεκριμένου ήχου γυρίζει στο 0 μόρια (προεπιλογή).

Δεν αλλάζει το μέγεθος γραμμάτων. Τι να ελέγξω;

  • Άνοιξε Ρυθμίσεις και επιβεβαίωσε ότι η τιμή άλλαξε σε ένα από τα επιτρεπτά βήματα (20/40/60/80/100).
  • Έλεγξε ότι δεν δοκιμάζεις την ίδια τιμή με πριν (αν μείνει ίδια, δεν αλλάζει scale).
  • Αν υπάρχει παλαιά τιμή εκτός επιτρεπτών βημάτων, το app την κανονικοποιεί αυτόματα στο κοντινότερο επιτρεπτό βήμα.

Δεν αλλάζει η γλώσσα εφαρμογής. Τι να ελέγξω;

  • Στην πρώτη εκκίνηση, πρέπει να ολοκληρωθεί και το βήμα επιβεβαίωσης μετά την επιλογή (Ναι) για να αποθηκευτεί η γλώσσα.
  • Στις Ρυθμίσεις, η αλλαγή γλώσσας εφαρμόζεται μόνο όταν επιβεβαιωθεί το σχετικό dialog.
  • Έλεγξε ότι η νέα επιλογή είναι διαφορετική από την τρέχουσα (Τρέχουσα γλώσσα: ...), αλλιώς τα κουμπιά δεν εφαρμόζουν αλλαγή.

Έγινε delete/cleanup και έμειναν “ορφανά” δεδομένα. Τι κάνουμε;

  • Δεν υπάρχουν cloud resources για cleanup.
  • Για local build artifacts, χρησιμοποίησε διαγραφή φακέλων app/build/ και build-artifacts/release/ αν χρειάζεται reset.

Γιατί δεν δημοσιεύεται release όταν κάνω push;

  • Το script κάνει direct publish με gh; έλεγξε πρώτα ότι υπάρχει ενεργό gh auth login.
  • Αν χρησιμοποιείς --skip-gh-release, τότε η δημοσίευση εξαρτάται από το workflow tag trigger v*.*.*.
  • Έλεγξε ότι έγινε push και του tag (git push origin vX.Y.Z), όχι μόνο branch.
  • Αν έχει ήδη γίνει direct publish από το script, το fallback workflow μπορεί να εμφανίσει skip στο publish step επειδή υπάρχει ήδη apk-release.apk στο release του tag (αναμενόμενο, όχι σφάλμα).

Γιατί εμφανίζει "App not installed as package appears to be invalid";

  • Συνήθως το APK είναι unsigned (ή αλλιώς αλλοιωμένο κατά το download).
  • Από εδώ και πέρα το pipeline αποτυγχάνει όταν λείπουν signing secrets και δεν ανεβάζει unsigned apk-release.apk.
  • Έλεγξε στο GitHub Release ότι κατεβάζεις το artifact apk-release.apk του τελευταίου επιτυχημένου release.

Το SHA256SUMS.txt έχει secrets και πρέπει να αφαιρείται;

  • Όχι, ένα αρχείο SHA256 checksums περιέχει μόνο hashes αρχείων και όχι κωδικούς/keys.
  • Παρ' όλα αυτά, το release policy του project πλέον ανεβάζει μόνο apk-release.apk ως custom asset για απλούστερη δημόσια διανομή.

Γιατί αποτυγχάνει το Analyze (java-kotlin) στο CodeQL;

  • Συνήθης αιτία είναι αποτυχία του CodeQL autobuild σε Android projects (δεν ανιχνεύει σωστά Gradle build βήματα).
  • Η ροή του project έχει οριστεί σε manual build mode με JDK 17 και compile-only command ./gradlew --no-daemon :app:compileDebugKotlin.
  • Αν ξανασπάσει, έλεγξε logs για JDK version mismatch ή σφάλμα Gradle dependency resolution.

Πώς κρύβω ευαίσθητα στοιχεία ώστε να μη φαίνονται στον κώδικα;

  • Βάλε τα σε Settings -> Secrets and variables -> Actions στο GitHub repository.
  • Για signing χρησιμοποίησε τα ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD.
  • Για αυτοματοποιημένη αρχική ρύθμιση keystore+secrets χρησιμοποίησε ./scripts/setup-release-signing.sh --set-github-secrets.
  • Το scripts/check-no-secrets.sh και το CI μπλοκάρουν release όταν εντοπίσουν committed secret files/keys.

Πού βρίσκω τα signing keys και μπορώ να τα βάλω στο build.gradle.kts;

  • Τα signing keys δεν υπάρχουν έτοιμα στο project, δημιουργούνται μία φορά με keytool.
  • Δεν τα βάζουμε ποτέ στο app/build.gradle.kts επειδή το repository είναι public.
  • Το project διαβάζει signing δεδομένα μόνο από environment variables και GitHub Secrets.

Υπάρχει μόνιμος έλεγχος ασφάλειας για αυτό το public repository;

  • Ναι, σε κάθε push/PR εκτελείται το workflow Security Guard.
  • Επιπλέον, πριν από κάθε release εκτελείται ξανά ο ίδιος έλεγχος και αν αποτύχει, το release μπλοκάρεται.
  • Για μέγιστη προστασία ενεργοποίησε στο GitHub branch protection με required status check το Security Guard / repository-secrets-guard.

Πώς επηρεάζονται άλλα components;

  • app/build.gradle.kts: προστέθηκε conditional release signing από environment variables.
  • scripts/bump-version.sh: χειρίζεται versionName/versionCode bump.
  • scripts/release-and-tag.sh: χτίζει release artifacts, απαιτεί υποχρεωτικά signing env vars, μπλοκάρει unsigned APK outputs, κάνει commit/tag/push, κάνει stage+commit όλες τις αλλαγές του working tree σε ένα release commit με σύντομο summary, παράγει user-friendly RELEASE_NOTES.md (previous tag → νέο tag) χωρίς διπλό τίτλο, και δημιουργεί/ενημερώνει direct GitHub Release μόνο με custom asset apk-release.apk.
  • scripts/check-no-secrets.sh: αποτρέπει commit/release όταν υπάρχουν tracked μυστικά ή υπογεγραμμένα κλειδιά μέσα στο repository.
  • scripts/setup-release-signing.sh: δημιουργεί release keystore εκτός repository, γράφει local env file signing και ενημερώνει προαιρετικά αυτόματα τα GitHub Actions secrets.
  • .github/workflows/security-guard.yml: τρέχει secrets guard σε κάθε push/PR.
  • .github/workflows/android-release.yml: τρέχει secrets guard, απαιτεί υποχρεωτικά signing secrets, κάνει package signed APK σε σταθερό alias apk-release.apk, ελέγχει αν υπάρχει ήδη ίδιο custom asset στο release του tag και κάνει skip το fallback publish όταν υπάρχει ήδη.
  • MainActivity και layout_main_activity.xml: προστέθηκε footer poweredby JohnChourp v.<version> με τιμή από BuildConfig.VERSION_NAME.
  • MainActivity και layout_main_activity.xml: προστέθηκε και νέο entry button Σημειώσεις για μετάβαση στη NotesActivity.
  • RecordingsActivity και RecordingsManagerActivity: πλήρης μετάβαση σε Compose UI με ViewModel/StateFlow, με τη σελίδα ηχογραφήσεων να δείχνει μόνο 10 local own recordings και τη διαχείριση να διατηρεί search+filters+sort.
  • recordings/index/*: νέο data layer με Room index (recordings_index.db), BFS indexer SAF, WorkManager reindex worker και repository για observe/operations.
  • recordings/ui/* και recordings/ui/components/*: νέα composables για recent list/manager list, row action menus και searchable Move to... επιλογή προορισμού.
  • RecordingFormatOption, RecordingsPrefs, AudioTranscoder: νέα helper modules για formats, persist settings φακέλου/μορφής και transcode μέσω FFmpeg.
  • OwnedRecordingsStore, RecordingDocumentOps και RecordingModels: local own-recordings history, κοινά μοντέλα + ασφαλής λογική rename fallback (direct rename και fallback copy/delete για αρχεία) με νέο outcome καταργήθηκε.
  • RecordingExternalOpener και byzantine_scan_file_paths.xml: ασφαλές άνοιγμα ηχογράφησης με audio-only MIME fallbacks (resolved -> audio/*) και dual URI strategy (δοκιμή original SAF URI και cache FileProvider URI) για μεγαλύτερη συμβατότητα σε Android 15 συσκευές.
  • notes/*: νέο notes module με Room (notes.db), repository/viewmodel, JSON snapshot codec, pending backup queue σε internal storage και SAF sync στον φάκελο που επιλέγει ο χρήστης.
  • NotesActivity και notes/ui/NotesScreen.kt: νέα Compose οθόνη με search, editor (τίτλος + κείμενο), auto-save, manual save, export/import και actions επανασυγχρονισμού.
  • AndroidManifest.xml: προστέθηκαν activities RecordingsActivity, RecordingsManagerActivity και permission RECORD_AUDIO.
  • strings.xml: προστέθηκαν labels/status/errors και περιγραφές για όλα τα διαθέσιμα formats ηχογράφησης.
  • SettingsActivity, layout_settings.xml, AppFontScale και BaseActivity: διαχειρίζονται την αποθήκευση/εφαρμογή global font scaling για όλη την εφαρμογή.
  • EightModesActivity, layout_eight_modes.xml, ScaleDiagramView και PhthongTonePlayer: διαχειρίζονται touch labels, αναπαραγωγή συχνοτήτων (Νη = 220Hz), επιλογή ηχοχρώματος με 3 presets (Καθαρός, Μαλακός, Κρυστάλλινο) και ρυθμιζόμενη μεταφορά βάσης ανά ήχο (-12..+12 μόρια) με μόνιμη αποθήκευση.
  • eight_modes_base_shift_card_bg.xml: ορίζει το visual styling της κάρτας μεταφοράς βάσης.
  • ByzantineScanActivity: υλοποιεί camera/gallery ροή, επιλογή ήχου/βάσης και προβολή αποτελεσμάτων αναγνώρισης.
  • ByzantineMelodyAnalyzer + BinaryImageOps: υλοποιούν adaptive preprocessing, αποκοπή πρώτης γραμμής, segmentation και recognition σε events base+modifier.
  • ByzantineRhythmMapper: εφαρμόζει κανόνες διάρκειας (κλάσμα, αντικένωμα+απλή, γοργό με redistribution χρόνου).
  • byzantine_core_symbol_rules_v2.json, byzantine_mode_rules_v1.json, byzantine_display_names_v1.json: ορίζουν core symbol rules, mode-aware trajectory profiles και ονόματα εμφάνισης.
  • scripts/generate-mk-symbol-dataset.py: δημιουργεί templates PNG και catalog JSON από MK/fonts + KeyBoard.ini.
  • app/src/main/assets/byzantine_interval_mapping_v1.json: διατηρεί το παραμετροποιήσιμο mapping token -> κίνηση φθόγγου.

Παραδείγματα

Happy path

{
  "command": "./scripts/release-and-tag.sh --bump patch",
  "tag": "v1.0.3",
  "release_assets": ["apk-release.apk", "Source code (zip)", "Source code (tar.gz)"]
}

Happy path (recordings)

{
  "folder_selected": true,
  "recording_state": "stopped",
  "saved_file": "recording_20260220_104500.flac",
  "visible_in_list": true
}

Failure example

{
  "error": "gh_auth_missing",
  "message": "Δεν υπάρχει ενεργό gh auth session. Τρέξε gh auth login ή χρησιμοποίησε --skip-gh-release."
}

Failure example (recordings)

{
  "error": "recording_transcode_failed",
  "message": "Αποτυχία αποθήκευσης ηχογράφησης.",
  "action": "έλεγχος format επιλογής και retry εγγραφής"
}

Γιατί η ανάλυση δεν αναγνωρίζει πάντα όλα τα σύμβολα;

  • Το MVP αναλύει μόνο το πρώτο μουσικό block/γραμμή.
  • Η αναγνώριση βασίζεται σε core template matching + semantic rules και επηρεάζεται από φωτισμό, θόρυβο και κλίση.
  • Τα mk_symbol_templates_v1 παραμένουν fallback/debug μέχρι να ολοκληρωθεί πλήρες MK token→glyph fix.
  • Για καλύτερα αποτελέσματα: καθαρή φωτογραφία, σταθερό χέρι και κοντινό κάδρο στο μουσικό απόσπασμα.
  • Χρησιμοποίησε πρώτα τη live περικοπή και την περιστροφή για να ευθυγραμμίσεις τη μουσική γραμμή πριν πατήσεις Ανάλυση.

Γιατί κρασάρει όταν πατάω Ανάλυση;

  • Προηγούμενη αιτία ήταν ανάλυση HARDWARE bitmap χωρίς επιτρεπτή πρόσβαση pixels.
  • Η ροή πλέον μετατρέπει την εικόνα σε software bitmap και προστατεύει την ανάλυση με ασφαλές error handling.
  • Αν αποτύχει η ανάλυση, εμφανίζεται μήνυμα αποτυχίας χωρίς να κλείνει η εφαρμογή.

Ενεργοποιείται auto-rotate όταν ανοίγω κάμερα;

  • Όχι. Η εφαρμογή είναι κλειδωμένη σε portrait orientation.
  • Η λήψη γίνεται από in-app camera activity (όχι εξωτερική camera app), επίσης κλειδωμένη σε portrait.

Γιατί ο ήχος στο Ημερολόγιο δεν ξεκινά από 1 Ιανουαρίου;

  • Ο υπολογισμός δεν βασίζεται σε σταθερή πολιτική ημερομηνία.
  • Η αρχή κύκλου υπολογίζεται ανά έτος από Ορθόδοξο Πάσχα και Πεντηκοστή.
  • Το app ξεκινά τον Α’ Ήχο στη 2η Κυριακή μετά την Πεντηκοστή και μετά κάνει κυκλική εναλλαγή ανά εβδομάδα (8 ήχοι).

Πώς επιλέγω γρήγορα μακρινό μήνα/έτος στο Ημερολόγιο;

  • Πάτησε πάνω στον τίτλο μήνα (π.χ. Μάρτιος 2026) για να ανοίξει απευθείας ο picker μήνα/έτους.
  • Επέλεξε μήνα και έτος από τους δύο pickers και πάτησε Επιλογή.
  • Αν θέλεις επιστροφή στο σήμερα, πάτησε το κουμπί Σήμερα.
  • Τα βελάκια παραμένουν διαθέσιμα για διαδοχική μετακίνηση μήνα-μήνα.

Πώς γεμίζω νέο μήνα στο εορτολόγιο;

  • Τρέξε από το root του project:
    • ./scripts/fill_calendar_month.py YYYY-MM
    • ή ./scripts/fill_calendar_month.py DD-MM-YYYY
  • Παράδειγμα:
    • ./scripts/fill_calendar_month.py 2025-01
    • ./scripts/fill_calendar_month.py 01-01-2025
  • Το script ενημερώνει μόνο τον μήνα που δήλωσες και κρατά ανέπαφους τους άλλους μήνες.
  • Για protected ημερομηνίες (days) ισχύει no-overwrite: αν υπάρχουν ήδη μένουν ως έχουν, αν λείπουν προστίθενται αυτόματα.
  • Το dataset γράφεται στο:
    • app/src/main/assets/calendar_celebrations_v1.json
  • Για κάθε ημερήσιο ανάγνωσμα, τα Νεοελληνικά γεμίζουν με πολιτική provider (Κολιτσάρα -> Τρεμπέλα -> επιτρεπτός provider-3 -> generated fallback).
  • Μόνο αν αποτύχουν όλες οι επιτρεπτές επιλογές εφαρμόζεται generated απλοποίηση.
  • Καμία πληροφορία πηγής (source/domain/url) δεν αποθηκεύεται ή εμφανίζεται στην εφαρμογή.

Πώς κάνω bootstrap τις protected ημερομηνίες για πολλά έτη;

  • Χρησιμοποίησε:
    • ./scripts/seed_protected_days.py START_YEAR END_YEAR
  • Παράδειγμα:
    • ./scripts/seed_protected_days.py 2025 2026
  • Το script δεν κάνει overwrite σε υπάρχουσες protected εγγραφές και κάνει insert μόνο όπου λείπουν.

Γιατί η σελίδα Σάρωσης δεν ανοίγει;

  • Η σελίδα Σάρωση Βυζαντινού Κειμένου είναι προσωρινά απενεργοποιημένη.
  • Το κουμπί αφαιρέθηκε από την αρχική και η activity επιστρέφει άμεσα με μήνυμα αν επιχειρηθεί άμεσο άνοιγμα.

Γιατί βλέπω μήνυμα «καταργήθηκε» όταν ανοίγω/μετονομάζω/διαγράφω ηχογράφηση;

  • Το μήνυμα εμφανίζεται όταν το αρχείο έχει διαγραφεί ή μετακινηθεί εκτός εφαρμογής και το URI δεν υπάρχει πλέον.
  • Η εφαρμογή αφαιρεί αυτόματα την εγγραφή από τη λίστα ώστε να μην ξαναεμφανιστεί σαν έγκυρο στοιχείο.
  • Η συμπεριφορά ισχύει τόσο στη recent λίστα όσο και στη διαχείριση ηχογραφήσεων.

Στη «Διαχείριση ηχογραφήσεων» λέει «δεν υπάρχουν», ενώ υπάρχουν nested φάκελοι. Τι να ελέγξω;

  • Η εφαρμογή πλέον κάνει normalization του SAF URI (tree -> document) πριν τα queries του current/root path.
  • Αυτό επιτρέπει να εμφανίζονται σωστά φάκελοι που δεν έχουν άμεσα audio files αλλά περιέχουν υποφακέλους.
  • Αν επιμένει το πρόβλημα, κάνε αλλαγή φακέλου (re-grant) και ξανάνοιξε τη διαχείριση για νέο reindex.

Πώς αλλάζω τον χαρακτήρα ήχου στη σελίδα 8 Ήχοι;

  • Πάνω από την κλίμακα υπάρχει νέο πεδίο Επιλογή ηχοχρώματος.
  • Μπορείς να επιλέξεις ανάμεσα σε Καθαρός, Μαλακός, Κρυστάλλινο.
  • Η επιλογή αποθηκεύεται και παραμένει ίδια μετά από κλείσιμο/άνοιγμα της εφαρμογής.

Γιατί δεν ακούγεται «ανθρώπινη» φωνή ακόμα;

  • Η τρέχουσα υλοποίηση είναι η Φάση 1: συνθετικά ηχοχρώματα για γρήγορο A/B test χωρίς licensing ρίσκο.
  • Η Φάση 2 προβλέπει sample-based ήχους μόνο CC0/Public Domain, αφού επιλέξεις ποια 1-2 ηχοχρώματα θέλεις να κρατήσουμε.
  • Μέχρι να κλειδώσουν τα τελικά samples, όλα τα πατήματα φθόγγων παίζουν από τον offline synth engine.

About

# LearnByzantineMusic This app will help you learn Byzantine music in Greek and English Language.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors