From a241807d4238765ba8578c5952e2dc1f9da441f3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 09:48:26 +0000 Subject: [PATCH] feat: Add comprehensive documentation to the entire codebase This commit introduces complete KDoc docstrings to all public classes, methods, and functions across the application. It also adds a comprehensive README.md file from scratch, detailing the project's purpose, features, setup, and usage instructions to provide a complete guide for new developers. --- README.md | 50 +++++++++++++++++++ .../example/ebookreader/AddNoteActivity.kt | 21 ++++++++ .../main/java/com/example/ebookreader/Book.kt | 5 ++ .../com/example/ebookreader/BookAdapter.kt | 34 +++++++++++++ .../com/example/ebookreader/MainActivity.kt | 41 +++++++++++++++ .../example/ebookreader/MindMapActivity.kt | 15 ++++++ .../main/java/com/example/ebookreader/Note.kt | 6 +++ .../com/example/ebookreader/NotesActivity.kt | 22 ++++++++ .../com/example/ebookreader/NotesAdapter.kt | 43 ++++++++++++++++ .../com/example/ebookreader/PdfAnnotation.kt | 8 +++ .../example/ebookreader/PdfViewerActivity.kt | 45 +++++++++++++++++ .../java/com/example/ebookreader/Utils.kt | 9 ++++ 12 files changed, 299 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bfb312 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# EBookReader + +EBookReader is a feature-rich Android application for reading and interacting with EPUB and PDF files. It provides a seamless reading experience with functionalities for highlighting text, taking notes, and visualizing notes as a mind map. + +## Features + +* **Dual Format Support**: Open and read both EPUB and PDF files. +* **Recent Books**: Your recently opened books are saved for quick access. +* **File Picker**: Browse and open ebooks from your device's storage. +* **EPUB Highlighting**: Highlight important sections in your EPUBs. +* **Note-Taking**: Add notes to any book. +* **Mind Maps**: Generate a mind map from your notes to visualize key concepts. +* **PDF Annotation**: Add annotations directly onto your PDF documents. + +## Setup + +To build and run this project, you'll need Android Studio. + +1. **Clone the repository:** + ```bash + git clone https://github.com/your-username/ebook-reader.git + ``` +2. **Open in Android Studio:** + Open Android Studio, select 'Open an existing Android Studio project', and navigate to the cloned repository folder. +3. **Build the project:** + Android Studio should automatically sync the Gradle project. If not, you can manually trigger a sync by going to `File > Sync Project with Gradle Files`. +4. **Run the application:** + You can run the application on an Android emulator or a physical device. Select your desired run configuration and click the 'Run' button. + +## Usage + +### Opening a Book + +You can open a book in two ways: + +1. **From Assets**: The application comes with pre-packaged books in the `assets` folder. These will be displayed on the main screen when you first launch the app. +2. **From Device Storage**: Click the 'Browse' button to open a file picker and select an `.epub` or `.pdf` file from your device. + +### Reading and Interaction + +* **EPUBs**: When you open an EPUB file, you can highlight text by long-pressing and selecting the desired text. +* **PDFs**: For PDF files, you can enable 'Annotation Mode' to add text annotations by tapping on the document. + +### Notes and Mind Maps + +For any book, you can: + +* **View/Add Notes**: Click the 'Notes' button next to a book on the main screen to view existing notes or add new ones. +* **Summarize Notes**: In the notes view, you can see a summary of all your notes. +* **Generate Mind Map**: Also in the notes view, you can generate a visual mind map from your notes. diff --git a/app/src/main/java/com/example/ebookreader/AddNoteActivity.kt b/app/src/main/java/com/example/ebookreader/AddNoteActivity.kt index 1d7e7f9..5c22d12 100644 --- a/app/src/main/java/com/example/ebookreader/AddNoteActivity.kt +++ b/app/src/main/java/com/example/ebookreader/AddNoteActivity.kt @@ -13,10 +13,19 @@ import java.io.FileWriter import java.text.SimpleDateFormat import java.util.* +/** + * This activity allows the user to add a new note to a specific book. + * It provides a simple interface with an EditText for the note text and a save button. + */ class AddNoteActivity : AppCompatActivity() { private var bookTitle: String? = null + /** + * Initializes the activity, sets up the UI, and handles the save button click. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Otherwise it is null. + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_note) @@ -33,6 +42,12 @@ class AddNoteActivity : AppCompatActivity() { } } + /** + * Saves a new note to a file associated with the book. + * + * @param bookTitle The title of the book to which the note is being added. + * @param noteText The text of the note to be saved. + */ private fun saveNote(bookTitle: String, noteText: String) { val notes = loadNotes(bookTitle).toMutableList() val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US) @@ -52,6 +67,12 @@ class AddNoteActivity : AppCompatActivity() { } } + /** + * Loads the list of notes for a specific book from a JSON file. + * + * @param bookTitle The title of the book for which to load the notes. + * @return A list of Note objects. Returns an empty list if the file doesn't exist or an error occurs. + */ private fun loadNotes(bookTitle: String): List { try { val file = File(filesDir, "${Utils.sha256(bookTitle)}_notes.json") diff --git a/app/src/main/java/com/example/ebookreader/Book.kt b/app/src/main/java/com/example/ebookreader/Book.kt index 310c2bf..6717334 100644 --- a/app/src/main/java/com/example/ebookreader/Book.kt +++ b/app/src/main/java/com/example/ebookreader/Book.kt @@ -1,3 +1,8 @@ package com.example.ebookreader +/** + * Represents a book in the application. + * + * @property title The title of the book. This can be a file path or an asset name. + */ data class Book(val title: String) diff --git a/app/src/main/java/com/example/ebookreader/BookAdapter.kt b/app/src/main/java/com/example/ebookreader/BookAdapter.kt index 8ea7b4b..0970fa0 100644 --- a/app/src/main/java/com/example/ebookreader/BookAdapter.kt +++ b/app/src/main/java/com/example/ebookreader/BookAdapter.kt @@ -7,15 +7,34 @@ import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +/** + * An adapter for displaying a list of books in a RecyclerView. + * + * @param books The list of books to be displayed. + * @param onItemClick A lambda function to be invoked when an item is clicked. The boolean parameter is true if the notes button is clicked, false otherwise. + */ class BookAdapter(private val books: List, private val onItemClick: (Book, Boolean) -> Unit) : RecyclerView.Adapter() { + /** + * Creates a new ViewHolder for a book item. + * + * @param parent The ViewGroup into which the new View will be added after it is bound to an adapter position. + * @param viewType The view type of the new View. + * @return A new BookViewHolder that holds a View of the given view type. + */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.book_item, parent, false) return BookViewHolder(view) } + /** + * Binds the data to the ViewHolder at the specified position. + * + * @param holder The ViewHolder which should be updated to represent the contents of the item at the given position in the data set. + * @param position The position of the item within the adapter's data set. + */ override fun onBindViewHolder(holder: BookViewHolder, position: Int) { val book = books[position] holder.bind(book) @@ -25,11 +44,26 @@ class BookAdapter(private val books: List, private val onItemClick: (Book, } } + /** + * Returns the total number of items in the data set held by the adapter. + * + * @return The total number of items in this adapter. + */ override fun getItemCount(): Int = books.size + /** + * A ViewHolder for a book item in the RecyclerView. + * + * @param itemView The view for the book item. + */ class BookViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val titleTextView: TextView = itemView.findViewById(R.id.book_title) + /** + * Binds a book to the ViewHolder. + * + * @param book The book to be displayed. + */ fun bind(book: Book) { titleTextView.text = book.title } diff --git a/app/src/main/java/com/example/ebookreader/MainActivity.kt b/app/src/main/java/com/example/ebookreader/MainActivity.kt index 054b7e4..c0326ae 100644 --- a/app/src/main/java/com/example/ebookreader/MainActivity.kt +++ b/app/src/main/java/com/example/ebookreader/MainActivity.kt @@ -19,6 +19,11 @@ import java.io.File import java.io.FileReader import java.io.FileWriter +/** + * The main entry point of the application. + * This activity displays a list of recent and asset-based books. It allows users to browse for new books from their device. + * It also handles the opening of EPUB and PDF files and manages highlights and recent book lists. + */ class MainActivity : AppCompatActivity() { private val PREFS_NAME = "EBookReaderPrefs" @@ -50,6 +55,11 @@ class MainActivity : AppCompatActivity() { } } + /** + * Initializes the activity, sets up the RecyclerView for books, and handles UI interactions. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Otherwise it is null. + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -95,6 +105,9 @@ class MainActivity : AppCompatActivity() { } } + /** + * Opens the system's file picker to allow the user to select an EPUB or PDF file. + */ private fun openFilePicker() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -104,6 +117,11 @@ class MainActivity : AppCompatActivity() { filePickerLauncher.launch(intent) } + /** + * Saves a highlight to a JSON file specific to the book. + * + * @param highlight The highlight object to be saved. + */ private fun saveHighlight(highlight: HighLight) { val highlights = loadHighlights(highlight.bookId).toMutableList() highlights.add(highlight) @@ -120,6 +138,12 @@ class MainActivity : AppCompatActivity() { } } + /** + * Loads highlights for a specific book from its JSON file. + * + * @param bookId The unique identifier for the book. + * @return A list of HighLight objects. Returns an empty list if the file doesn't exist or an error occurs. + */ private fun loadHighlights(bookId: String): List { try { val file = File(filesDir, "${Utils.sha256(bookId)}_highlights.json") @@ -135,12 +159,22 @@ class MainActivity : AppCompatActivity() { return emptyList() } + /** + * Saves the list of recent books to SharedPreferences. + * + * @param books The list of books to be saved. + */ private fun saveRecentBooks(books: List) { val gson = Gson() val json = gson.toJson(books) prefs.edit().putString(RECENT_BOOKS_KEY, json).apply() } + /** + * Loads the list of recent books from SharedPreferences. + * + * @return A list of recent Book objects. Returns an empty list if no recent books are found. + */ private fun loadRecentBooks(): List { val gson = Gson() val json = prefs.getString(RECENT_BOOKS_KEY, null) @@ -152,6 +186,13 @@ class MainActivity : AppCompatActivity() { } } + /** + * Adds a book to the top of the recent books list. + * If the book is already in the list, it is not added again. + * The list is capped at a maximum size defined by `MAX_RECENT_BOOKS`. + * + * @param book The book to be added. + */ private fun addRecentBook(book: Book) { if (!recentBooks.contains(book)) { recentBooks.add(0, book) diff --git a/app/src/main/java/com/example/ebookreader/MindMapActivity.kt b/app/src/main/java/com/example/ebookreader/MindMapActivity.kt index 17f2681..d8197cb 100644 --- a/app/src/main/java/com/example/ebookreader/MindMapActivity.kt +++ b/app/src/main/java/com/example/ebookreader/MindMapActivity.kt @@ -9,8 +9,17 @@ import com.google.gson.reflect.TypeToken import java.io.File import java.io.FileReader +/** + * This activity displays a mind map generated from the notes of a specific book. + * It uses a WebView to render the mind map using the markmap-autoloader.js library. + */ class MindMapActivity : AppCompatActivity() { + /** + * Initializes the activity, sets up the WebView, and loads the mind map data. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Otherwise it is null. + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_mind_map) @@ -47,6 +56,12 @@ class MindMapActivity : AppCompatActivity() { } } + /** + * Loads the list of notes for a specific book from a JSON file. + * + * @param bookTitle The title of the book for which to load the notes. + * @return A list of Note objects. Returns an empty list if the file doesn't exist or an error occurs. + */ private fun loadNotes(bookTitle: String): List { try { val file = File(filesDir, "${Utils.sha256(bookTitle)}_notes.json") diff --git a/app/src/main/java/com/example/ebookreader/Note.kt b/app/src/main/java/com/example/ebookreader/Note.kt index af7f9bf..27ef60f 100644 --- a/app/src/main/java/com/example/ebookreader/Note.kt +++ b/app/src/main/java/com/example/ebookreader/Note.kt @@ -1,3 +1,9 @@ package com.example.ebookreader +/** + * Represents a single note taken by the user for a book. + * + * @property text The content of the note. + * @property timestamp The date and time when the note was created, in ISO 8601 format. + */ data class Note(val text: String, val timestamp: String) diff --git a/app/src/main/java/com/example/ebookreader/NotesActivity.kt b/app/src/main/java/com/example/ebookreader/NotesActivity.kt index a31271a..ab4631e 100644 --- a/app/src/main/java/com/example/ebookreader/NotesActivity.kt +++ b/app/src/main/java/com/example/ebookreader/NotesActivity.kt @@ -13,12 +13,21 @@ import com.google.gson.reflect.TypeToken import java.io.File import java.io.FileReader +/** + * This activity displays a list of notes for a specific book. + * It allows the user to add new notes, view a summary of existing notes, and generate a mind map. + */ class NotesActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private lateinit var notesAdapter: NotesAdapter private var bookTitle: String? = null + /** + * Initializes the activity, sets up the RecyclerView for notes, and handles UI interactions. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Otherwise it is null. + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_notes) @@ -53,11 +62,18 @@ class NotesActivity : AppCompatActivity() { } } + /** + * Called when the activity will start interacting with the user. + * This is a good place to begin animations, open exclusive-access devices (such as the camera), etc. + */ override fun onResume() { super.onResume() loadNotes() } + /** + * Loads the notes for the current book and updates the RecyclerView. + */ private fun loadNotes() { if (bookTitle != null) { val notes = loadNotesFromFile(bookTitle!!) @@ -65,6 +81,12 @@ class NotesActivity : AppCompatActivity() { } } + /** + * Loads the list of notes for a specific book from a JSON file. + * + * @param bookTitle The title of the book for which to load the notes. + * @return A list of Note objects. Returns an empty list if the file doesn't exist or an error occurs. + */ private fun loadNotesFromFile(bookTitle: String): List { try { val file = File(filesDir, "${Utils.sha256(bookTitle)}_notes.json") diff --git a/app/src/main/java/com/example/ebookreader/NotesAdapter.kt b/app/src/main/java/com/example/ebookreader/NotesAdapter.kt index fdf100a..2d24d3c 100644 --- a/app/src/main/java/com/example/ebookreader/NotesAdapter.kt +++ b/app/src/main/java/com/example/ebookreader/NotesAdapter.kt @@ -6,34 +6,77 @@ import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +/** + * An adapter for displaying a list of notes in a RecyclerView. + * + * @param notes The initial list of notes to be displayed. + */ class NotesAdapter(private var notes: List) : RecyclerView.Adapter() { + /** + * Updates the list of notes and notifies the adapter of the data change. + * + * @param newNotes The new list of notes to be displayed. + */ fun updateNotes(newNotes: List) { notes = newNotes notifyDataSetChanged() } + /** + * Returns the current list of notes. + * + * @return The current list of notes. + */ fun getNotes(): List { return notes } + /** + * Creates a new ViewHolder for a note item. + * + * @param parent The ViewGroup into which the new View will be added after it is bound to an adapter position. + * @param viewType The view type of the new View. + * @return A new NoteViewHolder that holds a View of the given view type. + */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.note_item, parent, false) return NoteViewHolder(view) } + /** + * Binds the data to the ViewHolder at the specified position. + * + * @param holder The ViewHolder which should be updated to represent the contents of the item at the given position in the data set. + * @param position The position of the item within the adapter's data set. + */ override fun onBindViewHolder(holder: NoteViewHolder, position: Int) { holder.bind(notes[position]) } + /** + * Returns the total number of items in the data set held by the adapter. + * + * @return The total number of items in this adapter. + */ override fun getItemCount(): Int = notes.size + /** + * A ViewHolder for a note item in the RecyclerView. + * + * @param itemView The view for the note item. + */ class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val noteText: TextView = itemView.findViewById(R.id.note_text) private val noteTimestamp: TextView = itemView.findViewById(R.id.note_timestamp) + /** + * Binds a note to the ViewHolder. + * + * @param note The note to be displayed. + */ fun bind(note: Note) { noteText.text = note.text noteTimestamp.text = note.timestamp diff --git a/app/src/main/java/com/example/ebookreader/PdfAnnotation.kt b/app/src/main/java/com/example/ebookreader/PdfAnnotation.kt index 0b8cfbc..6a5029e 100644 --- a/app/src/main/java/com/example/ebookreader/PdfAnnotation.kt +++ b/app/src/main/java/com/example/ebookreader/PdfAnnotation.kt @@ -1,3 +1,11 @@ package com.example.ebookreader +/** + * Represents a single annotation on a PDF document. + * + * @property page The page number where the annotation is located. + * @property x The x-coordinate of the annotation, as a fraction of the page width. + * @property y The y-coordinate of the annotation, as a fraction of the page height. + * @property text The text content of the annotation. + */ data class PdfAnnotation(val page: Int, val x: Float, val y: Float, val text: String) diff --git a/app/src/main/java/com/example/ebookreader/PdfViewerActivity.kt b/app/src/main/java/com/example/ebookreader/PdfViewerActivity.kt index d0203e6..fd59baf 100644 --- a/app/src/main/java/com/example/ebookreader/PdfViewerActivity.kt +++ b/app/src/main/java/com/example/ebookreader/PdfViewerActivity.kt @@ -19,6 +19,11 @@ import java.io.File import java.io.FileReader import java.io.FileWriter +/** + * This activity is responsible for displaying PDF documents and handling annotations. + * It uses the AndroidPdfViewer library to render the PDF and provides functionality + * for adding, displaying, and saving annotations. + */ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { private lateinit var pdfView: PDFView @@ -27,6 +32,11 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { private var bookTitle: String? = null private var annotationMode = false + /** + * Initializes the activity, sets up the PDF viewer, loads the document, and handles UI interactions. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Otherwise it is null. + */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_pdf_viewer) @@ -66,6 +76,14 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { } } + /** + * Called when a layer is drawn on the PDF view. This is used to draw the annotations on the canvas. + * + * @param canvas The canvas on which to draw. + * @param pageWidth The width of the page. + * @param pageHeight The height of the page. + * @param displayedPage The index of the currently displayed page. + */ override fun onLayerDrawn(canvas: Canvas?, pageWidth: Float, pageHeight: Float, displayedPage: Int) { val paint = Paint() paint.color = Color.RED @@ -77,6 +95,12 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { } } + /** + * Called when the user taps on the PDF view. If in annotation mode, it triggers the annotation dialog. + * + * @param event The MotionEvent object containing full information about the event. + * @return True if the event was handled, false otherwise. + */ override fun onTap(event: android.view.MotionEvent?): Boolean { if (annotationMode && event != null) { showAnnotationDialog(event.x, event.y) @@ -84,6 +108,12 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { return true } + /** + * Displays a dialog to get the text for a new annotation. + * + * @param x The x-coordinate of the tap event. + * @param y The y-coordinate of the tap event. + */ private fun showAnnotationDialog(x: Float, y: Float) { val editText = EditText(this) AlertDialog.Builder(this) @@ -101,6 +131,9 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { .show() } + /** + * Saves the current list of annotations to a JSON file. + */ private fun saveAnnotations() { val gson = Gson() val json = gson.toJson(annotations) @@ -115,6 +148,12 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { } } + /** + * Loads annotations from a JSON file. + * + * @param fileName The name of the file from which to load the annotations. + * @return A list of PdfAnnotation objects. Returns an empty list if the file doesn't exist or an error occurs. + */ private fun loadAnnotations(fileName: String): List { try { val file = File(filesDir, "${getFileName()}_annotations.json") @@ -129,6 +168,12 @@ class PdfViewerActivity : AppCompatActivity(), OnDrawListener, OnTapListener { return emptyList() } + /** + * Generates a unique file name for the current book using a SHA-256 hash. + * This is used for storing and retrieving annotations and notes specific to the book. + * + * @return A SHA-256 hash of the book's URI or title. + */ private fun getFileName(): String { val name = if (bookUriString != null) bookUriString else bookTitle return Utils.sha256(name!!) diff --git a/app/src/main/java/com/example/ebookreader/Utils.kt b/app/src/main/java/com/example/ebookreader/Utils.kt index b2234e9..9e4a925 100644 --- a/app/src/main/java/com/example/ebookreader/Utils.kt +++ b/app/src/main/java/com/example/ebookreader/Utils.kt @@ -2,7 +2,16 @@ package com.example.ebookreader import java.security.MessageDigest +/** + * A utility object that provides common helper functions. + */ object Utils { + /** + * Computes the SHA-256 hash of a given string. + * + * @param base The string to be hashed. + * @return The hexadecimal representation of the SHA-256 hash. + */ fun sha256(base: String): String { val digest = MessageDigest.getInstance("SHA-256") val hash = digest.digest(base.toByteArray(Charsets.UTF_8))