Skip to content

Commit 2b5f47a

Browse files
authored
Merge pull request #2 from SurjitSahoo/enhancements
feat: Introduce book detail screen, global player, smart rewind settings, and enhance content caching and player UI.
2 parents 5735933 + 51068cf commit 2b5f47a

File tree

87 files changed

+5207
-1066
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+5207
-1066
lines changed

.agent/rules/project-context.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
trigger: always_on
3+
---
4+
5+
# Project Context and Guidelines
6+
7+
## About Project
8+
9+
This is an offline-first Android client app for the [audiobookshelf](https://github.com/advplyr/audiobookshelf) server.
10+
11+
## Feature Requirements (Offline first behavior)
12+
13+
- As the app is an offline-first app, assume that the server is not always reachable.
14+
- Playback progress should be saved in the local database first **immediately**.
15+
- The app should watch the network changes, such as connecting to a new wifi network, or Ethernet LAN, or disconnecting from a network and connecting to a new network, disconnecting from wifi and connecting to celular network, etc., and try to ping or reach out to the audiobook server to check whether the server is reachable.
16+
- If the server is reachable, the app should sync the local progress to the server and pull the latest progress updates from the server to the local database; it should merge the updates from both.
17+
- If the server is reachable, and some chapters of any audiobook are downloaded, i.e., available offline, then the offline track should be given priority for playback.
18+
- 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.
19+
- The app must be fully functional for downloaded content when offline.
20+
21+
## Ensure Stability
22+
23+
- Ensure null-safety when converting data (e.g., check for division by zero in percentage calculations).
24+
- Changes must be verified by building the app and ensuring logic holds (e.g., uninstall/reinstall for clean state tests).
25+
26+
## Overall Functionality
27+
28+
- When the app loads, it should load the offline available book content immediately, then in the background reach out to the server (if the server is reachable) and fetch the full list of the books, continue listening section books and update the UI seemlessly.
29+
- The app should cache all the book's metadata in local database to optimise the app load time, Only the chapters / audio tracks should not be cached automatically / by default. Chapters should be downloaded and cached on demand by the user, using the download chapters/book functionality.
30+
- When the app loads, if the server is not reachable, it shouldn't show long loading screen, trying to fetch the books from the server, it should load the book's list from the local database, however it should only show the books whos' chapters are downloaded and available offline can be played.
31+
- When the server becomes reachable, it should update the books list, as now all the books can be played from local cache or online from the server.
32+
- When the network is switched, the app should trigger checking whether the server is still reachable or not, if not reachable, it should update the UI to only show offline available ready to play books.
33+
34+
## General Guidelines and Standards
35+
36+
- **Colors**: Must be referenced from `Color.kt` / `Theme.kt`. Do not use raw hex values (e.g., `0xFF...`) in Composables.
37+
- **Dimensions**: Must use `Spacing.kt` (e.g., `Spacing.md`, `Spacing.lg`) for padding, margins, and layout dimensions.
38+
- **Design System**: Adhere effectively to the spacing system and color palette defined in the project.
39+
- **ABSOLUTELY NO** hardcoded user-facing strings in UI code. All strings must be extracted to `strings.xml` and accessed via `stringResource`.
40+
- Use `associateBy` or proper indexing for collection lookups (O(1)) instead of nested loops (O(N^2)) when synchronizing data.
41+
- Avoid expensive operations on the main thread.
42+
- No code duplication, keep the code clean and easy to maintain.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Auto Version Bump
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths-ignore:
8+
- 'gradle.properties'
9+
- '.github/workflows/**'
10+
11+
jobs:
12+
bump-version:
13+
runs-on: ubuntu-latest
14+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
15+
steps:
16+
- name: Checkout Code
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Get Current Version
23+
id: get_version
24+
run: |
25+
VERSION=$(grep 'appVersionName=' gradle.properties | cut -d'=' -f2)
26+
echo "current_version=$VERSION" >> $GITHUB_OUTPUT
27+
28+
- name: Determine Bump Type
29+
id: bump_type
30+
uses: anothrNick/github-tag-action@1.67.0
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
WITH_V: false
34+
DRY_RUN: true
35+
DEFAULT_BUMP: patch
36+
37+
- name: Bump Version in Files
38+
id: bump
39+
run: |
40+
BUMP_TYPE="${{ steps.bump_type.outputs.part }}"
41+
if [ -z "$BUMP_TYPE" ]; then BUMP_TYPE="patch"; fi
42+
NEW_VERSION=$(python3 scripts/bump_version.py ${{ steps.get_version.outputs.current_version }} $BUMP_TYPE)
43+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
44+
45+
- name: Commit and Push Changes
46+
run: |
47+
NEW_TAG="v${{ steps.bump.outputs.new_version }}"
48+
git config --local user.email "action@github.com"
49+
git config --local user.name "GitHub Action"
50+
git add gradle.properties
51+
git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }} [skip ci]"
52+
git tag $NEW_TAG
53+
git push origin main
54+
git push origin $NEW_TAG

.github/workflows/build_app.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
name: Build Lissen App
1+
name: Build Kahani App
22

33
env:
44
# The name of the main module repository
55
main_project_module: app
66

77
on:
88
push:
9-
branches: [ "main" ]
9+
branches: ['main']
1010
pull_request:
11-
branches: [ "main" ]
11+
branches: ['main']
1212

1313
workflow_dispatch:
1414

1515
jobs:
1616
build:
17-
1817
runs-on: ubuntu-latest
1918

2019
steps:
@@ -37,6 +36,9 @@ jobs:
3736
- name: Change wrapper permissions
3837
run: chmod +x ./gradlew
3938

39+
- name: Create Schemas Directory
40+
run: mkdir -p app/schemas
41+
4042
# Run Build Project
4143
- name: Build gradle project
42-
run: ./gradlew build -Proom.schemaLocation=$GITHUB_WORKSPACE/app/schemas
44+
run: ./gradlew build

.github/workflows/release.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Build and Release Kahani
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
9+
jobs:
10+
release:
11+
name: Build Signed APK
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout Code
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Set up JDK 21
20+
uses: actions/setup-java@v4
21+
with:
22+
distribution: 'zulu'
23+
java-version: '21'
24+
cache: 'gradle'
25+
26+
- name: Create Schemas Directory
27+
run: mkdir -p app/schemas
28+
29+
- name: Decode Keystore
30+
run: |
31+
echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > app/kahani-release.jks
32+
33+
- name: Build Release APK
34+
run: ./gradlew assembleRelease
35+
env:
36+
RELEASE_STORE_FILE: kahani-release.jks
37+
RELEASE_STORE_PASSWORD: ${{ secrets.RELEASE_PASSWORD }}
38+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
39+
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
40+
41+
- name: Create Release
42+
uses: softprops/action-gh-release@v1
43+
with:
44+
files: app/build/outputs/apk/release/app-release.apk
45+
tag_name: ${{ github.ref_name }}
46+
name: Release ${{ github.ref_name }}
47+
body: |
48+
Automated Release for Kahani.
49+
Commit: ${{ github.sha }}
50+
draft: false
51+
prerelease: false
52+
env:
53+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ google-services.json
3434
*.hprof
3535

3636
# Schemas
37-
app/schemas
37+
.DS_Store

.vscode/tasks.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "gradle",
6+
"id": "${workspaceFolder}app:installDebugapp",
7+
"script": "app:installDebug",
8+
"description": "Installs the Debug build.",
9+
"group": "install",
10+
"project": "app",
11+
"buildFile": "${workspaceFolder}/app/build.gradle.kts",
12+
"rootProject": "Lissen",
13+
"projectFolder": "${workspaceFolder}",
14+
"workspaceFolder": "${workspaceFolder}",
15+
"args": "",
16+
"javaDebug": false,
17+
"problemMatcher": ["$gradle"],
18+
"label": "gradle: app:installDebug"
19+
}
20+
]
21+
}

app/build.gradle.kts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ android {
5050
defaultConfig {
5151
val commitHash = gitCommitHash()
5252

53-
applicationId = "org.grakovne.lissen"
53+
applicationId = "org.surjit.kahani"
5454
minSdk = 28
5555
targetSdk = 36
56-
versionCode = 10800
57-
versionName = "1.8.0-$commitHash"
56+
versionCode = project.property("appVersionCode").toString().toInt()
57+
versionName = project.property("appVersionName").toString()
5858

5959
buildConfigField("String", "GIT_HASH", "\"$commitHash\"")
6060

@@ -66,27 +66,36 @@ android {
6666
buildConfigField("String", "ACRA_REPORT_LOGIN", "\"$acraReportLogin\"")
6767
buildConfigField("String", "ACRA_REPORT_PASSWORD", "\"$acraReportPassword\"")
6868

69-
if (project.hasProperty("RELEASE_STORE_FILE")) {
70-
signingConfigs {
71-
create("release") {
72-
storeFile = file(project.property("RELEASE_STORE_FILE")!!)
73-
storePassword = project.property("RELEASE_STORE_PASSWORD") as String?
74-
keyAlias = project.property("RELEASE_KEY_ALIAS") as String?
75-
keyPassword = project.property("RELEASE_KEY_PASSWORD") as String?
76-
enableV1Signing = true
77-
enableV2Signing = true
69+
signingConfigs {
70+
create("release") {
71+
val envKeyStore = System.getenv("RELEASE_STORE_FILE")
72+
val propKeyStore = localProperties.getProperty("RELEASE_STORE_FILE")
73+
74+
storeFile = when {
75+
envKeyStore != null -> file(envKeyStore)
76+
propKeyStore != null -> file(propKeyStore)
77+
else -> null
7878
}
79+
80+
storePassword = System.getenv("RELEASE_STORE_PASSWORD") ?: localProperties.getProperty("RELEASE_STORE_PASSWORD")
81+
keyAlias = System.getenv("RELEASE_KEY_ALIAS") ?: localProperties.getProperty("RELEASE_KEY_ALIAS")
82+
keyPassword = System.getenv("RELEASE_KEY_PASSWORD") ?: localProperties.getProperty("RELEASE_KEY_PASSWORD")
83+
84+
enableV1Signing = true
85+
enableV2Signing = true
7986
}
8087
}
8188
}
8289

83-
8490
buildTypes {
8591
release {
86-
if (project.hasProperty("RELEASE_STORE_FILE")) {
87-
signingConfig = signingConfigs.getByName("release")
92+
val releaseSigningConfig = signingConfigs.getByName("release")
93+
94+
if (releaseSigningConfig.storeFile?.exists() == true) {
95+
signingConfig = releaseSigningConfig
8896
}
89-
isMinifyEnabled = false
97+
98+
isMinifyEnabled = true
9099
isShrinkResources = false
91100
proguardFiles(
92101
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"

0 commit comments

Comments
 (0)