Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ktorClientCoreVersion = "3.3.3"
ktx_version = "1.17.0"
appcompat_version = "1.7.1"
material_version = "1.13.0"
material_icons = "1.7.8"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

latest stable androidx.room version 2025 2026

💡 Result:

The latest stable AndroidX Room release is 2.8.4 (released November 19, 2025). [1][2]

Sources:
[1] Android Developers — Room release notes.
[2] Maven Repository — androidx.room:room-runtime (latest).


🌐 Web query:

latest stable KSP Kotlin Symbol Processing version 2025 2026

💡 Result:

The latest stable KSP release is 2.3.4 (released Dec 16, 2025). [1][2]

Sources:

  • google/ksp releases (GitHub) — 2.3.4. [1]
  • Kotlin KSP documentation (Kotlinlang) — KSP2 notes. [2]

🌐 Web query:

latest stable Glide image loading library version 2025 2026

💡 Result:

Latest stable Glide (bumptech/glide) release is v5.0.5 (released Sep 13, 2025). [1][2]

Sources:

  • bumptech/glide Releases (v5.0.5). [1]
  • Glide documentation / download page. [2]

🌐 Web query:

latest stable androidx material icons version 2025 2026

💡 Result:

Latest stable androidx Material Icons: 1.7.8 (released Feb 12, 2025). [1][2]

Note: the androidx.compose.material.icons library is no longer recommended (Material Symbols preferred). [1]

Sources:

  • AndroidX Compose Material release notes (library deprecation). [1]
  • Maven / artifact listing for material-icons-extended / material-icons-core (1.7.8). [2]

Update KSP to 2.3.4 and consider Material Symbols instead of Material Icons.

Room 2.8.4, Glide 5.0.5, and Material Icons 1.7.8 are latest stable. However, KSP 2.0.4 is outdated; the latest stable is 2.3.4 (released Dec 16, 2025). Update the KSP version accordingly.

Additionally, androidx.compose.material.icons (Material Icons) is deprecated in favor of Material Symbols. If possible, migrate to Material Symbols for future compatibility.

🤖 Prompt for AI Agents
In @gradle/libs.versions.toml at line 11, Update the KSP version entry in
libs.versions.toml (the ksp = ... entry) from 2.0.4 to 2.3.4 to pick up the
latest stable KSP; also replace or supplement the material_icons = "1.7.8" entry
by migrating to Material Symbols (introduce a material_symbols entry and update
any dependencies and imports that reference androidx.compose.material.icons to
the corresponding Material Symbols artifact and APIs) so the project uses the
newer symbols library for future compatibility.

lifecycle_runtime_ktx = "2.10.0"
picassoVersion = "2.71828"
smartExceptionJavaVersion = "0.2.1"
Expand All @@ -29,6 +30,9 @@ composeBomVersion = "2025.12.00"
navigationComposeVersion = "2.9.6"
ktlint = "14.0.1"
runtimeVersion = "1.10.0"
room_version = "2.8.4"
kspversion = "2.2.21-2.0.4"
glide = "5.0.5"

[libraries]
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayoutVersion" }
Expand All @@ -40,7 +44,7 @@ filament-android = { module = "com.google.android.filament:filament-android", ve
filament-utils-android = { module = "com.google.android.filament:filament-utils-android", version.ref = "filamentAndroidVersion" }
gltfio-android = { module = "com.google.android.filament:gltfio-android", version.ref = "filamentAndroidVersion" }
google-android-material = { group = "com.google.android.material", name = "material", version.ref = "material_version" }

material-icons = {group = "androidx.compose.material", name="material-icons-core", version.ref = "material_icons"}
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle_runtime_ktx" }
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work_version" }

Expand All @@ -56,6 +60,7 @@ ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negoti
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCoreVersion" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorClientCoreVersion" }
picasso = { module = "com.squareup.picasso:picasso", version.ref = "picassoVersion" }
glide = {module="com.github.bumptech.glide:glide", version.ref="glide" }
smart-exception-java = { module = "com.arthenica:smart-exception-java", version.ref = "smartExceptionJavaVersion" }
tensorflow-lite = { module = "org.tensorflow:tensorflow-lite", version.ref = "tensorflowLiteVersion" }
tensorflow-lite-gpu-delegate-plugin = { module = "org.tensorflow:tensorflow-lite-gpu-delegate-plugin", version.ref = "tensorflowLiteTaskVisionVersion" }
Expand All @@ -78,6 +83,12 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationComposeVersion" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "runtimeVersion" }

#Room
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room_version" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
androidx-room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room_version" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agpVersion" }
android-library = { id = "com.android.library", version.ref = "agpVersion" }
Expand All @@ -86,6 +97,7 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlinVersion" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinVersion" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
ksp = { id = "com.google.devtools.ksp", version.ref="kspversion"}

[bundles]
androidx = [
Expand Down
1 change: 1 addition & 0 deletions modules/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat
implementation libs.google.android.material
implementation libs.material.icons

implementation libs.androidx.lifecycle.runtime.ktx
implementation libs.androidx.work.runtime.ktx
Expand Down
1 change: 1 addition & 0 deletions modules/datastore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
50 changes: 50 additions & 0 deletions modules/datastore/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.ksp)
id 'maven-publish'
}

android {
namespace 'com.tejpratapsingh.motion.datastore'
compileSdk 36

defaultConfig {
minSdk 25

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
}

afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
}
}
}
}

dependencies {
api libs.androidx.room.runtime
ksp libs.androidx.room.compiler
api libs.androidx.room.ktx
testImplementation libs.androidx.room.testing
}
Empty file.
21 changes: 21 additions & 0 deletions modules/datastore/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Comment on lines +1 to +21
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add Room-specific ProGuard rules.

The datastore module contains Room entities and DAOs that must be preserved during code shrinking and obfuscation. Without proper ProGuard/R8 rules, release builds will fail at runtime with reflection errors.

🔎 Recommended ProGuard rules for Room
 # Add project specific ProGuard rules here.
 # You can control the set of applied configuration files using the
 # proguardFiles setting in build.gradle.
 #
 # For more details, see
 #   http://developer.android.com/guide/developing/tools/proguard.html
 
+# Room-specific rules
+-keep class * extends androidx.room.RoomDatabase
+-keep @androidx.room.Entity class *
+-dontwarn androidx.room.paging.**
+
 # If your project uses WebView with JS, uncomment the following
 # and specify the fully qualified class name to the JavaScript interface
 # class:
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
 
 # Uncomment this to preserve the line number information for
 # debugging stack traces.
 #-keepattributes SourceFile,LineNumberTable
 
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
 #-renamesourcefileattribute SourceFile
🤖 Prompt for AI Agents
In modules/datastore/proguard-rules.pro around lines 1 to 21, add ProGuard/R8
rules that preserve Room entities, DAOs and generated database classes so they
aren’t removed/obfuscated at build time: keep all classes annotated with
androidx.room.Entity, androidx.room.Dao and androidx.room.Database (and their
members used via reflection), keep Room-generated implementation classes (Room_*
and any Impl classes), retain Room annotations and metadata (kotlin.Metadata if
using Kotlin), and preserve any constructors or fields accessed reflectively by
Room; add these entries to this file so release builds won’t fail at runtime due
to missing Room classes.

2 changes: 2 additions & 0 deletions modules/datastore/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tejpratapsingh.motion.datastore.data.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.tejpratapsingh.motion.datastore.data.entity.ProjectEntity

@Dao
interface ProjectDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(projectEntity: ProjectEntity)

@Query("SELECT * FROM projects")
suspend fun getAll(): List<ProjectEntity>

Choose a reason for hiding this comment

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

medium

For better integration with Compose and to build a reactive UI, consider returning a Flow<List<ProjectEntity>> from this function instead of a List<ProjectEntity>. This will allow your UI to automatically observe changes in the database and update itself without manual data reloading.

Suggested change
suspend fun getAll(): List<ProjectEntity>
suspend fun getAll(): Flow<List<ProjectEntity>>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.tejpratapsingh.motion.datastore.data.database

import android.content.Context
import androidx.room.Room

object DatabaseProvider {
@Volatile
private var INSTANCE: ProjectDatabase? = null

fun getDatabase(context: Context): ProjectDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
ProjectDatabase::class.java,
"LyricMaker"
).build()
Comment on lines +12 to +16
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add database migration strategy to prevent crashes on schema changes.

The Room database is built without a migration strategy. If the schema changes in future releases, the app will crash when users upgrade. You must either:

  • Add .fallbackToDestructiveMigration() for development/testing (destroys data on schema changes)
  • Implement proper migrations with .addMigrations() for production (preserves user data)
🔧 Proposed fix options

Option 1: For development (destroys data on schema changes):

 val instance = Room.databaseBuilder(
     context.applicationContext,
     ProjectDatabase::class.java,
     "LyricMaker"
-)
+)
+.fallbackToDestructiveMigration()
 .build()

Option 2: For production (preserves data with migrations):

 val instance = Room.databaseBuilder(
     context.applicationContext,
     ProjectDatabase::class.java,
     "LyricMaker"
-)
+)
+.addMigrations(MIGRATION_1_2, MIGRATION_2_3, /* etc */)
 .build()

You'll need to define migration objects separately. See Room Migration documentation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val instance = Room.databaseBuilder(
context.applicationContext,
ProjectDatabase::class.java,
"LyricMaker"
).build()
val instance = Room.databaseBuilder(
context.applicationContext,
ProjectDatabase::class.java,
"LyricMaker"
)
.fallbackToDestructiveMigration()
.build()
🤖 Prompt for AI Agents
In
@modules/datastore/src/main/java/com/tejpratapsingh/motion/datastore/data/database/DatabaseProvider.kt
around lines 12 - 16, The Room database is built without a migration strategy in
DatabaseProvider when constructing ProjectDatabase (the instance created via
Room.databaseBuilder), causing upgrade crashes; update the builder chain to
either call .fallbackToDestructiveMigration() for dev/testing or supply
.addMigrations(...) with defined Migration objects for production (create
Migration instances targeting the appropriate version numbers and pass them to
addMigrations), ensuring the change is applied on the same builder that produces
the ProjectDatabase instance.

INSTANCE = instance
instance
}
}
Comment on lines +10 to +20
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add database migration strategy.

The database builder doesn't specify a migration strategy. When you update the schema in future versions (e.g., adding columns, new tables), the app will crash unless you provide migrations or explicitly allow destructive migrations.

🔎 Recommended approach

For development/MVP, add fallback to destructive migration:

 fun getDatabase(context: Context): ProjectDatabase {
     return INSTANCE ?: synchronized(this) {
         val instance = Room.databaseBuilder(
             context.applicationContext,
             ProjectDatabase::class.java,
             "LyricMaker"
-        ).build()
+        )
+            .fallbackToDestructiveMigration()
+            .build()
         INSTANCE = instance
         instance
     }
 }

Note: For production, replace with proper migrations to preserve user data:

.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun getDatabase(context: Context): ProjectDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
ProjectDatabase::class.java,
"LyricMaker"
).build()
INSTANCE = instance
instance
}
}
fun getDatabase(context: Context): ProjectDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
ProjectDatabase::class.java,
"LyricMaker"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
instance
}
}
🤖 Prompt for AI Agents
In
modules/datastore/src/main/java/com/tejpratapsingh/motion/datastore/data/database/DatabaseProvider.kt
around lines 10 to 20, the Room database is built without any migration strategy
which will crash the app on schema changes; update the builder to either
register proper migrations with .addMigrations(...) for production or, for
development/MVP, add .fallbackToDestructiveMigration() to avoid crashes, and
ensure you declare any Migration objects (e.g., MIGRATION_1_2) and pass them
into .addMigrations(...) when opting for non-destructive updates.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tejpratapsingh.motion.datastore.data.database

import androidx.room.Database
import androidx.room.RoomDatabase
import com.tejpratapsingh.motion.datastore.data.dao.ProjectDao
import com.tejpratapsingh.motion.datastore.data.entity.ProjectEntity

@Database(entities = [ProjectEntity::class], version = 1, exportSchema = false)

Choose a reason for hiding this comment

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

medium

It's a best practice to enable schema exporting for Room databases, especially in production apps. Setting exportSchema to true allows you to version control your database schema, which is invaluable for tracking history and managing migrations. You'll need to specify the schema location in your module's build.gradle file.

Suggested change
@Database(entities = [ProjectEntity::class], version = 1, exportSchema = false)
@Database(entities = [ProjectEntity::class], version = 1, exportSchema = true)

abstract class ProjectDatabase : RoomDatabase() {
abstract fun projectDao(): ProjectDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tejpratapsingh.motion.datastore.data.entity

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "projects")
data class ProjectEntity(
@PrimaryKey(autoGenerate = true)
val id: Long = 0L,
val title: String?,
val description: String?,
val image: String?,
val siteName: String?,
val twitterCard: String?,
val url: String?,
val trackName: String?,
val artistName: String?,
val albumName: String?,
val duration: Float?,
val instrumental: Boolean?,
val plainLyrics: String?,
val syncedLyrics: String?,
val totalLyrics: String?,
val selectedLyrics: String?,
val savedFilePath: String?,
)
Comment on lines 6 to 26
Copy link
Contributor

Choose a reason for hiding this comment

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

Add database migration strategy. The database is created without exportSchema = false but no migration handling is implemented. This will cause crashes when the schema changes in future updates.

Suggested change
@Entity(tableName = "projects")
data class ProjectEntity(
@PrimaryKey(autoGenerate = true)
val id: Long = 0L,
val title: String?,
val description: String?,
val image: String?,
val siteName: String?,
val twitterCard: String?,
val url: String?,
val trackName: String?,
val artistName: String?,
val albumName: String?,
val duration: Float?,
val instrumental: Boolean?,
val plainLyrics: String?,
val syncedLyrics: String?,
val savedFilePath: String?,
)
@Entity(tableName = "projects")
data class ProjectEntity(
@PrimaryKey(autoGenerate = true)
val id: Long = 0L,
val title: String?,
val description: String?,
val image: String?,
val siteName: String?,
val twitterCard: String?,
val url: String?,
val trackName: String?,
val artistName: String?,
val albumName: String?,
val duration: Float?,
val instrumental: Boolean?,
val plainLyrics: String?,
val syncedLyrics: String?,
val savedFilePath: String?,
val createdAt: Long = System.currentTimeMillis(), // Add timestamp for better data management
)

Comment on lines 7 to 26

Choose a reason for hiding this comment

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

medium

All properties in this data class are nullable. While this provides flexibility, it can also lead to a high number of null checks throughout the codebase and potentially allow for inconsistent or incomplete data to be saved. Consider making fields that should always be present non-nullable and providing default values if appropriate. This will make your data model more robust and your code safer.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.tejpratapsingh.motion.metadataextractor
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the package name to match the module structure.

The package declaration is com.tejpratapsingh.motion.metadataextractor, but the file is located in the datastore module. This mismatch will cause compilation errors and incorrect imports.

🔎 Proposed fix
-package com.tejpratapsingh.motion.metadataextractor
+package com.tejpratapsingh.motion.datastore
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package com.tejpratapsingh.motion.metadataextractor
package com.tejpratapsingh.motion.datastore
🤖 Prompt for AI Agents
In
modules/datastore/src/test/java/com/tejpratapsingh/motion/datastore/ExampleUnitTest.kt
around line 1, the file has package com.tejpratapsingh.motion.metadataextractor
which does not match the datastore module path; change the package declaration
to the datastore package (e.g., com.tejpratapsingh.motion.datastore) so it
matches the directory structure, and update any imports or references in the
test to use the corrected package.


import org.junit.Assert.*
import org.junit.Test

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
47 changes: 47 additions & 0 deletions modules/imageloader/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.ksp)
id 'maven-publish'
}

android {
namespace 'com.tejpratapsingh.motion.imageloader'
compileSdk 36

defaultConfig {
minSdk 25

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
}

afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
}
}
}
}

dependencies {
api libs.glide
}
Empty file.
21 changes: 21 additions & 0 deletions modules/imageloader/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
2 changes: 2 additions & 0 deletions modules/imageloader/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.tejpratapsingh.motion.imageloader

import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.util.Log
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

object ImageLoader {

fun loadImage(context: Context, url: String) =
Glide.with(context).load(url)

suspend fun loadBitmap(context: Context, url: String): Bitmap = suspendCancellableCoroutine { cont ->
val target = object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
cont.resume(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {
if (!cont.isCompleted) cont.cancel()
}
}

Glide.with(context)
.asBitmap()
.load(url)
.into(target)

cont.invokeOnCancellation {
Glide.with(context).clear(target)
}
}
Comment on lines +18 to +36
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add error handling for failed image loads.

The loadBitmap function lacks error handling. If Glide fails to load the image (network error, 404, invalid URL, etc.), the CustomTarget does not override onLoadFailed, so the coroutine continuation is never resumed, causing the caller to hang indefinitely.

🐛 Proposed fix to handle load failures
     suspend fun loadBitmap(context: Context, url: String): Bitmap = suspendCancellableCoroutine { cont ->
         val target = object : CustomTarget<Bitmap>() {
             override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                 cont.resume(resource)
             }
             override fun onLoadCleared(placeholder: Drawable?) {
                 if (!cont.isCompleted) cont.cancel()
             }
+            override fun onLoadFailed(errorDrawable: Drawable?) {
+                cont.cancel(Exception("Failed to load image from URL: $url"))
+            }
         }

         Glide.with(context)
             .asBitmap()
             .load(url)
             .into(target)

         cont.invokeOnCancellation {
             Glide.with(context).clear(target)
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
suspend fun loadBitmap(context: Context, url: String): Bitmap = suspendCancellableCoroutine { cont ->
val target = object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
cont.resume(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {
if (!cont.isCompleted) cont.cancel()
}
}
Glide.with(context)
.asBitmap()
.load(url)
.into(target)
cont.invokeOnCancellation {
Glide.with(context).clear(target)
}
}
suspend fun loadBitmap(context: Context, url: String): Bitmap = suspendCancellableCoroutine { cont ->
val target = object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
cont.resume(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {
if (!cont.isCompleted) cont.cancel()
}
override fun onLoadFailed(errorDrawable: Drawable?) {
cont.resumeWithException(Exception("Failed to load image from URL: $url"))
}
}
Glide.with(context)
.asBitmap()
.load(url)
.into(target)
cont.invokeOnCancellation {
Glide.with(context).clear(target)
}
}
🤖 Prompt for AI Agents
In
@modules/imageloader/src/main/java/com/tejpratapsingh/motion/imageloader/ImageLoader.kt
around lines 18 - 36, The coroutine in loadBitmap uses a CustomTarget but does
not override onLoadFailed, so failures never resume the continuation; update the
anonymous CustomTarget in loadBitmap to override onLoadFailed and call
cont.resumeWithException with an appropriate exception (or cont.cancel with a
Throwable) when Glide fails, keep onResourceReady calling cont.resume(resource)
and onLoadCleared cancelling if not completed, and ensure invokeOnCancellation
still clears the target via Glide.with(context).clear(target) so no leaks occur.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.tejpratapsingh.motion.imageloader

import org.junit.Assert.*
import org.junit.Test

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
5 changes: 3 additions & 2 deletions modules/lyrics-maker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,19 @@ dependencies {
implementation libs.ktor.client.content.negotiation
implementation libs.ktor.serialization.kotlinx.json

implementation libs.picasso

implementation project(path: ':modules:motionlib')
implementation project(path: ':modules:metadata-extractor')
implementation project(path: ':modules:ffmpeg-motion-ext')
implementation project(path: ':modules:ongoing')
implementation project(path: ':modules:imageloader')

implementation libs.androidx.work.runtime.ktx
implementation libs.gson

implementation platform(libs.androidx.compose.bom)
implementation libs.bundles.compose
implementation libs.androidx.navigation.compose
implementation libs.material.icons

testImplementation libs.junit
androidTestImplementation libs.androidx.test.ext.junit
Expand Down
Loading
Loading