Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3dae591
feat: Implement database migration to version 16 with a dedicated UI,…
Feb 3, 2026
9d0f06a
feat: Filter cached book queries to only include books that have at l…
Feb 3, 2026
71cbf62
feat: enhance cached items screen with storage statistics, detailed b…
Feb 4, 2026
01f3521
feat: introduce a new download modal with enhanced options for segmen…
Feb 4, 2026
714d9b2
feat: Allow subtitles to span two lines and refine download modal mon…
Feb 4, 2026
de72283
feat: Introduce `Queued` cache status, replace the shimmering downloa…
Feb 4, 2026
a106485
feat: enhance caching progress reporting with notification throttling…
Feb 4, 2026
41717a8
feat: Remove thumbnail cache clearing functionality and associated UI…
Feb 4, 2026
f936b5c
feat: Integrate Microsoft Clarity for analytics tracking and UI maski…
Feb 5, 2026
3a2ce89
feat: Implement analytics consent, overhaul persistent caching with d…
Feb 6, 2026
ecd3041
feat: Update CachedItemsSettingsScreen to navigate to the library whe…
Feb 6, 2026
331716d
feat: Implement bulk selection for cached items in settings, add meta…
Feb 6, 2026
dba6ec3
feat: Implement progressive image loading with blurred thumbnails, ad…
Feb 6, 2026
b8a4d87
Fix: Adjust `_preparingBookId` clearing to occur upon playback start …
Feb 6, 2026
684ec8c
feat: Optimize playback readiness by decoupling cover art updates, en…
Feb 6, 2026
5b66d4c
fix: forced server availability check, pull down to refresh, continue…
Feb 6, 2026
52c1e27
fix: show spinner for player buttons when the playback is being prepared
Feb 6, 2026
fc0fa44
fix: initial launch playback freeze by moving the exo player initiali…
Feb 6, 2026
74fc260
add changelog generation
Feb 6, 2026
826141a
Add update checker service
Feb 6, 2026
1b5bd36
fix book deletion glitch
Feb 6, 2026
cc16ea0
add code rabbit
Feb 6, 2026
1eefb8f
feat: Prevent analytics consent bottom sheet dismissal by swipe and r…
Feb 7, 2026
a509960
feat: Implement structured concurrency for playback operations and re…
Feb 7, 2026
5b3247a
feat: Implement animated download completion effect and theme-aware i…
Feb 7, 2026
a05001c
feat: Implement an animated download success shine effect in `Downloa…
Feb 7, 2026
1d7160e
feat: Refactor `VerticalScrollbar` to accept a lazy color lambda and …
Feb 7, 2026
b31fcce
feat: Display a loading indicator and disable the delete button while…
Feb 7, 2026
6833e85
feat: Display a loading indicator in BookDetailScreen while book deta…
Feb 7, 2026
9e7d44b
refactor: delegate book metadata caching to `LocalCacheRepository` an…
Feb 7, 2026
229ed16
feat: Migrate crash reporting from ACRA to Firebase Crashlytics and i…
Feb 7, 2026
6ddf4e2
feat: Add Firebase Crashlytics exception reporting to various error h…
Feb 7, 2026
e1833a4
feat: Implement initial retry logic for server availability checks wi…
Feb 7, 2026
92828f4
feat: Implement optimized library synchronization using `updatedAt` t…
Feb 7, 2026
073c97d
fix: Improve analytics robustness, enhance playback service thread sa…
Feb 7, 2026
382da19
fix: correct typo from "app_crach_toast" to "app_crash_toast" in mult…
Feb 7, 2026
49ec6f3
chore: bump app version to 1.1.0 and update version code
Feb 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .agent/rules/project-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ This is an offline-first Android client app for the [audiobookshelf](https://git
- If the offline track is deleted/cleared while the book is being played, the player should attempt to fallback to the online URL if the server is reachable, otherwise pause playback and persist the last playback state (track ID, playback position/timestamp, current chapter/index, and playback status) plus a flag indicating offline content was removed; ensure the rule notes that these persisted fields are used to resume or report playback state and are written atomically to the player state store.
- The app must be fully functional for downloaded content when offline.

## Future Plans (Not to be implemented now)
- The app should support multi-server / multi-user connections. A user should be able to connect to a server, download a few books for offline listening, then disconnect and connect to a new server and download few more books, and listen to all the books offline, and then connecting back to the previous server should push (sync) progress updates from the books which were downloaded earlier for offline listening.
- The app should also support uploading books that were downloaded from a server to another server (sharing)
- The app should support importing audiobooks from the phone storage directly without any server connection. Optionally, the user should be able to upload these audiobooks to the server along with the progress.

## Ensure Stability

- Ensure null-safety when converting data (e.g., check for division by zero in percentage calculations).
Expand All @@ -40,4 +45,4 @@ This is an offline-first Android client app for the [audiobookshelf](https://git
- **ABSOLUTELY NO** hardcoded user-facing strings in UI code. All strings must be extracted to `strings.xml` and accessed via `stringResource`.
- Use `associateBy` or proper indexing for collection lookups (O(1)) instead of nested loops (O(N^2)) when synchronizing data.
- Avoid expensive operations on the main thread.
- No code duplication, keep the code clean and easy to maintain.
- No code duplication, keep the code clean and easy to maintain.
3 changes: 3 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: "en-US"
reviews:
auto_title_placeholder: "@coderabbit title"
31 changes: 27 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,38 @@ jobs:
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}

- name: Build Changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
configuration: |
{
"categories": [
{
"title": "## 🚀 Features",
"labels": ["feature", "enhancement"]
},
{
"title": "## 🐛 Bug Fixes",
"labels": ["fix", "bug"]
},
{
"title": "## 📦 Dependencies & Maintenance",
"labels": ["chore", "deps", "dependency", "refactor"]
}
],
"template": "{{CHANGELOG}}"
}

- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
files: app/build/outputs/apk/release/app-release.apk
tag_name: v${{ steps.get_version.outputs.version }}
name: Kahani v${{ steps.get_version.outputs.version }}
body: |
Automated Release for Kahani.
Commit: ${{ github.sha }}
body: ${{ steps.build_changelog.outputs.changelog }}
draft: false
prerelease: false
env:
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ render.experimental.xml
*.jks
*.keystore

# Google Services (e.g. APIs or Firebase)
google-services.json

# Android Profiling
*.hprof

Expand Down
18 changes: 9 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ plugins {
id("com.google.dagger.hilt.android")
id("org.jmailen.kotlinter") version "5.2.0"
id("com.google.devtools.ksp")
alias(libs.plugins.google.services)
alias(libs.plugins.firebase.crashlytics)
}

kotlinter {
Expand Down Expand Up @@ -60,11 +62,7 @@ android {

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

val acraReportLogin = "8yJ59n0UToCja8LR"
val acraReportPassword = "kuW9TV7BbJByuIAc"

buildConfigField("String", "ACRA_REPORT_LOGIN", "\"$acraReportLogin\"")
buildConfigField("String", "ACRA_REPORT_PASSWORD", "\"$acraReportPassword\"")
buildConfigField("String", "CLARITY_PROJECT_ID", "\"vc8bgk8nk9\"")

signingConfigs {
create("release") {
Expand Down Expand Up @@ -183,17 +181,19 @@ dependencies {
implementation(libs.androidx.glance.appwidget)
implementation(libs.androidx.glance.material3)

implementation(libs.acra.core)
implementation(libs.acra.http)
implementation(libs.acra.toast)

implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)

implementation(libs.converter.moshi)
implementation(libs.moshi)
implementation(libs.moshi.kotlin)

implementation(libs.microsoft.clarity)

implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics)
implementation(libs.firebase.analytics)

debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
48 changes: 48 additions & 0 deletions app/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "139954826172",
"project_id": "kahani-app-android",
"storage_bucket": "kahani-app-android.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:139954826172:android:0f4f76f1de010ba41699b9",
"android_client_info": {
"package_name": "com.kahani.app"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAB8o-65tB6TzUbdaqVAP9XnTl1wgC6dks"
}
Comment on lines +16 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

GCP API key committed to repository — ensure it is restricted in Cloud Console.

While google-services.json is routinely committed to Android projects (and Google documents it as safe to include in source control), the Firebase/GCP API key on Lines 18 and 37 is nonetheless visible to anyone with repo access. Static analysis (Gitleaks) correctly flags this.

Mitigations to apply:

  1. Restrict the API key in the Google Cloud Console → Credentials: limit it by Android app (package name + SHA-1 fingerprint) and by API (only the specific Firebase APIs you use).
  2. If this repository is public, also consider whether the project number and app IDs leak more surface area than intended — Firebase App Check can further lock down backend access.
  3. Ensure .gitignore patterns are intentional — some teams choose to distribute this file via CI secrets instead of checking it in for public repos.

No code change needed if the key is properly restricted, but leaving it unrestricted in a public repo is a security posture gap.

Also applies to: 35-38

🧰 Tools
🪛 Gitleaks (8.30.0)

[high] 18-18: Uncovered a GCP API key, which could lead to unauthorized access to Google Cloud services and data breaches.

(gcp-api-key)

🤖 Prompt for AI Agents
In `@app/google-services.json` around lines 16 - 19, The committed Firebase/GCP
API key is exposed in google-services.json under the "api_key" -> "current_key"
field; restrict that key in the Google Cloud Console (Credentials) by applying
Android app restrictions (package name + SHA-1) and API restrictions to only
needed Firebase APIs, and if this repo is public remove or stop committing
google-services.json (or serve it via CI secrets) and consider enabling Firebase
App Check for additional protection; also review .gitignore to prevent
accidental commits of google-services.json going forward.

],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:139954826172:android:1d7d97392c6e248f1699b9",
"android_client_info": {
"package_name": "com.kahani.app.debug"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAB8o-65tB6TzUbdaqVAP9XnTl1wgC6dks"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
6 changes: 5 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@
# Hilt and Dagger rules (usually bundled, but good to ensure)
-keep class dagger.hilt.android.internal.** { *; }
-keep class *__HiltBindingModule { *; }
-keep class org.grakovne.lissen.**_HiltComponents$* { *; }
-keep class org.grakovne.lissen.**_HiltComponents$* { *; }

# Microsoft Clarity
-keep class com.microsoft.clarity.** { *; }
-keep interface com.microsoft.clarity.** { *; }
Loading