diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..f4923b02
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,2 @@
+[*.{kt,kts}]
+ktlint_standard_function-naming = disabled
diff --git a/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml
index 091d0704..29e28e1c 100644
--- a/.github/workflows/ktlint.yml
+++ b/.github/workflows/ktlint.yml
@@ -27,3 +27,9 @@ jobs:
report-path: build/*.xml # Support glob patterns by https://www.npmjs.com/package/@actions/glob
ignore-warnings: true # Ignore Lint Warnings
continue-on-error: false # If annotations contain error of severity, action-ktlint exit 1.
+ - name: Upload ktlint report
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: ktlint-report
+ path: build/ktlint-report.xml
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index 8cc5d7c5..86cca123 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -27,6 +27,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/RenaultCar.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/RenaultCar.kt
index 7649c891..184668d7 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/RenaultCar.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/RenaultCar.kt
@@ -20,7 +20,7 @@ class RenaultCar(
effects: List = emptyList(),
) : BaseContourMotionView(context, startFrame, endFrame, effects = effects) {
companion object {
- const val imageAssetSubFolder = "renault_kiger_bg"
+ const val IMAGE_ASSET_SUB_FOLDER = "renault_kiger_bg"
}
private val imageView: ImageView =
@@ -78,7 +78,7 @@ class RenaultCar(
)
// Determine which image to show based on the current frame
- val imageName = "$imageAssetSubFolder/$frame.png"
+ val imageName = "$IMAGE_ASSET_SUB_FOLDER/$frame.png"
try {
val inputStream: InputStream = assetManager.open(imageName)
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionLayoutInfo.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionLayoutInfo.kt
index 064f15d7..3a8a9518 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionLayoutInfo.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionLayoutInfo.kt
@@ -12,20 +12,20 @@ data class MotionLayoutInfo(
val height: Int = WRAP_CONTENT,
val padding: Padding = Padding(),
val margin: Margin = Margin(),
- val gravity: Int = Gravity.NO_GRAVITY
+ val gravity: Int = Gravity.NO_GRAVITY,
) {
data class Padding(
val left: Int = 0,
val top: Int = 0,
val right: Int = 0,
- val bottom: Int = 0
+ val bottom: Int = 0,
)
data class Margin(
val left: Int = 0,
val top: Int = 0,
val right: Int = 0,
- val bottom: Int = 0
+ val bottom: Int = 0,
)
companion object {
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoAspectRatio.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoAspectRatio.kt
index cd031136..a8e7ef35 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoAspectRatio.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoAspectRatio.kt
@@ -1,5 +1,6 @@
package com.tejpratapsingh.motionlib.core
+@Suppress("ClassName")
sealed class VideoAspectRatio(
val width: Int,
val height: Int,
diff --git a/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/MotionTextSizeProviderTest.kt b/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/MotionTextSizeProviderTest.kt
index 3d963468..303000a3 100644
--- a/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/MotionTextSizeProviderTest.kt
+++ b/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/MotionTextSizeProviderTest.kt
@@ -5,7 +5,6 @@ import org.junit.Before
import org.junit.Test
class MotionTextSizeProviderTest {
-
@Before
fun setup() {
// Reset baseTextScale before each test
@@ -15,10 +14,10 @@ class MotionTextSizeProviderTest {
@Test
fun testDefaultFontSizes() {
val aspectRatio = VideoAspectRatio.Ratio16x9_1080 // 1920x1080, min is 1080, scale is 1.0
-
+
// H1 should be 160f * 1.0 = 160f
assertEquals(160f, MotionTextSizeProvider.getFontSize(aspectRatio, MotionTextVariant.H1), 0.01f)
-
+
// P should be 48f * 1.0 = 48f
assertEquals(48f, MotionTextSizeProvider.getFontSize(aspectRatio, MotionTextVariant.P), 0.01f)
}
@@ -26,22 +25,22 @@ class MotionTextSizeProviderTest {
@Test
fun testBaseTextScale() {
val aspectRatio = VideoAspectRatio.Ratio16x9_1080 // 1920x1080, min is 1080, scale is 1.0
-
+
MotionTextSizeProvider.baseTextScale = 2.0f
-
+
// H1 should be 160f * 2.0 = 320f
assertEquals(320f, MotionTextSizeProvider.getFontSize(aspectRatio, MotionTextVariant.H1), 0.01f)
-
+
// P should be 48f * 2.0 = 96f
assertEquals(96f, MotionTextSizeProvider.getFontSize(aspectRatio, MotionTextVariant.P), 0.01f)
}
@Test
fun testScalingWithAspectRatio() {
- // 480p 16:9 is 854x480, min is 480.
+ // 480p 16:9 is 854x480, min is 480.
// Scale = 480 / 1080 = 0.4444...
val aspectRatio = VideoAspectRatio.Ratio16x9_480
-
+
val expectedH1 = 160f * (480f / 1080f)
assertEquals(expectedH1, MotionTextSizeProvider.getFontSize(aspectRatio, MotionTextVariant.H1), 0.01f)
}
diff --git a/modules/ivi-demo/build.gradle b/modules/ivi-demo/build.gradle
index f4c316ee..aeac1f7b 100644
--- a/modules/ivi-demo/build.gradle
+++ b/modules/ivi-demo/build.gradle
@@ -3,11 +3,11 @@ plugins {
}
android {
- namespace 'com.tejpratapsingh.ivi_demo'
+ namespace 'com.tejpratapsingh.ividemo'
compileSdk 36
defaultConfig {
- applicationId "com.tejpratapsingh.ivi_demo"
+ applicationId "com.tejpratapsingh.ividemo"
minSdk 28
targetSdk 36
versionCode 1
diff --git a/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt b/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ividemo/ExampleInstrumentedTest.kt
similarity index 94%
rename from modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt
rename to modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ividemo/ExampleInstrumentedTest.kt
index 3333ff22..dfb122fc 100644
--- a/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt
+++ b/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ividemo/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.tejpratapsingh.ivi_demo
+package com.tejpratapsingh.ividemo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/MainActivity.kt
similarity index 85%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/MainActivity.kt
index 72bce271..fe325442 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/MainActivity.kt
@@ -1,9 +1,8 @@
-package com.tejpratapsingh.ivi_demo
+package com.tejpratapsingh.ividemo
-import RenaultCar
-import android.os.Build
import android.os.Bundle
-import com.tejpratapsingh.ivi_demo.extension.enableSwipeSeekReverse
+import com.tejpratapsingh.ividemo.extension.enableSwipeSeekReverse
+import com.tejpratapsingh.ividemo.motion.RenaultCar
import com.tejpratapsingh.motionlib.activities.PreviewActivity
import com.tejpratapsingh.motionlib.core.MotionConfig
import com.tejpratapsingh.motionlib.core.VideoAspectRatio
@@ -39,9 +38,7 @@ class MainActivity : PreviewActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- motionVideoPlayer.seekBar.min = 1
- }
+ motionVideoPlayer.seekBar.min = 1
motionView.enableSwipeSeekReverse(
maxProgress = video.totalFrames,
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/extension/ViewExtensions.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/extension/ViewExtensions.kt
similarity index 97%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/extension/ViewExtensions.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/extension/ViewExtensions.kt
index 5b88c362..99cd3159 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/extension/ViewExtensions.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/extension/ViewExtensions.kt
@@ -1,4 +1,4 @@
-package com.tejpratapsingh.ivi_demo.extension
+package com.tejpratapsingh.ividemo.extension
import android.annotation.SuppressLint
import android.view.MotionEvent
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/RenaultCar.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/RenaultCar.kt
similarity index 92%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/RenaultCar.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/RenaultCar.kt
index 7b752821..eb95ff78 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/RenaultCar.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/RenaultCar.kt
@@ -1,3 +1,5 @@
+package com.tejpratapsingh.ividemo.motion
+
import android.content.Context
import android.graphics.BitmapFactory
import android.widget.ImageView
@@ -22,8 +24,8 @@ class RenaultCar(
effects: List = emptyList(),
) : BaseContourMotionView(context, startFrame, endFrame, effects = effects) {
companion object {
- const val imageAssetSubFolder = "renault_kiger_bg"
- const val roadAssetSubFolder = "road"
+ const val IMAGE_ASSET_SUB_FOLDER = "renault_kiger_bg"
+ const val ROAD_ASSET_SUB_FOLDER = "road"
}
private val imageViewBg: ImageView =
@@ -37,8 +39,8 @@ class RenaultCar(
}
private val assetManager = context.assets
- private val files = assetManager.list(imageAssetSubFolder)
- private val roadFiles = assetManager.list(roadAssetSubFolder)
+ private val files = assetManager.list(IMAGE_ASSET_SUB_FOLDER)
+ private val roadFiles = assetManager.list(ROAD_ASSET_SUB_FOLDER)
init {
imageViewBg.layoutBy(
@@ -120,7 +122,7 @@ class RenaultCar(
// val road = String.format(
// Locale.getDefault(),
// "%s/%02d.png",
-// roadAssetSubFolder,
+// ROAD_ASSET_SUB_FOLDER,
// min(roadInterpolator, (roadFiles?.size ?: 1) - 1)
// )
//
@@ -137,7 +139,7 @@ class RenaultCar(
String.format(
Locale.getDefault(),
"%s/%d.png",
- imageAssetSubFolder,
+ IMAGE_ASSET_SUB_FOLDER,
min(frame, (files?.size ?: 1) - 1),
)
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/Road.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/Road.kt
similarity index 92%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/Road.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/Road.kt
index 95a8126a..dbd0b36c 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/Road.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/motion/Road.kt
@@ -1,4 +1,4 @@
-package com.tejpratapsingh.ivi_demo.motion
+package com.tejpratapsingh.ividemo.motion
import android.content.Context
import android.graphics.BitmapFactory
@@ -18,7 +18,7 @@ class Road(
effects: List = emptyList(),
) : BaseContourMotionView(context, startFrame, endFrame, effects = effects) {
companion object {
- const val imageAssetSubFolder = "road"
+ const val IMAGE_ASSET_SUB_FOLDER = "road"
}
private val imageView: ImageView =
@@ -60,7 +60,7 @@ class Road(
super.forFrame(frame)
// Determine which image to show based on the current frame
- val imageName = "$imageAssetSubFolder/$frame.png"
+ val imageName = "$IMAGE_ASSET_SUB_FOLDER/$frame.png"
try {
val inputStream: InputStream = assetManager.open(imageName)
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/sequence/RenaultSequence.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/sequence/RenaultSequence.kt
similarity index 87%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/sequence/RenaultSequence.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/sequence/RenaultSequence.kt
index 1b506627..27e84cb6 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/sequence/RenaultSequence.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/sequence/RenaultSequence.kt
@@ -1,7 +1,7 @@
-package com.tejpratapsingh.ivi_demo.sequence
+package com.tejpratapsingh.ividemo.sequence
-import RenaultCar
import android.content.Context
+import com.tejpratapsingh.ividemo.motion.RenaultCar
import com.tejpratapsingh.motionlib.core.MotionConfig
import com.tejpratapsingh.motionlib.core.VideoAspectRatio
import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
@@ -18,7 +18,7 @@ fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
setCurrentConfig(motionConfig)
val assetManager = applicationContext.assets
- val files = assetManager.list(RenaultCar.imageAssetSubFolder)
+ val files = assetManager.list(RenaultCar.IMAGE_ASSET_SUB_FOLDER)
val motionView: BaseContourMotionView =
RenaultCar(
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/view/TrapezoidImageView.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/view/TrapezoidImageView.kt
similarity index 97%
rename from modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/view/TrapezoidImageView.kt
rename to modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/view/TrapezoidImageView.kt
index 7298f227..aee0db11 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/view/TrapezoidImageView.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ividemo/view/TrapezoidImageView.kt
@@ -1,4 +1,4 @@
-package com.tejpratapsingh.ivi_demo.view
+package com.tejpratapsingh.ividemo.view
import android.content.Context
import android.graphics.Bitmap
diff --git a/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt b/modules/ivi-demo/src/test/java/com/tejpratapsingh/ividemo/ExampleUnitTest.kt
similarity index 90%
rename from modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt
rename to modules/ivi-demo/src/test/java/com/tejpratapsingh/ividemo/ExampleUnitTest.kt
index a3032286..3e79791a 100644
--- a/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt
+++ b/modules/ivi-demo/src/test/java/com/tejpratapsingh/ividemo/ExampleUnitTest.kt
@@ -1,4 +1,4 @@
-package com.tejpratapsingh.ivi_demo
+package com.tejpratapsingh.ividemo
import org.junit.Assert.assertEquals
import org.junit.Test
diff --git a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ProjectDetailsScreenTest.kt b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ProjectDetailsScreenTest.kt
index 74d9166b..bfa01bd8 100644
--- a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ProjectDetailsScreenTest.kt
+++ b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ProjectDetailsScreenTest.kt
@@ -28,7 +28,6 @@ import org.junit.runner.RunWith
*/
@RunWith(AndroidJUnit4::class)
class ProjectDetailsScreenTest {
-
@get:Rule
val composeTestRule = createComposeRule()
@@ -72,6 +71,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = {},
)
}
@@ -91,6 +91,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = {},
)
}
@@ -113,6 +114,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = {},
)
}
@@ -133,6 +135,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = {},
)
}
@@ -149,6 +152,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = { sharedProject = it },
)
}
@@ -170,6 +174,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = {},
+ onEditClick = {},
onShareClick = {},
)
}
@@ -186,6 +191,7 @@ class ProjectDetailsScreenTest {
ProjectDetailsScreen(
project = project,
onBackClick = { backClicked = true },
+ onEditClick = {},
onShareClick = {},
)
}
@@ -194,4 +200,43 @@ class ProjectDetailsScreenTest {
assertEquals(true, backClicked)
}
-}
\ No newline at end of file
+
+ // ------------------------------------------------------------------
+ // Edit button
+ // ------------------------------------------------------------------
+
+ @Test
+ fun editButton_isDisplayed() {
+ val project = buildProject()
+
+ composeTestRule.setContent {
+ ProjectDetailsScreen(
+ project = project,
+ onBackClick = {},
+ onEditClick = {},
+ onShareClick = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText("Edit Project").assertIsDisplayed()
+ }
+
+ @Test
+ fun editButton_invokesOnEditClickWithProject() {
+ val project = buildProject(id = "edit-test")
+ var editedProject: MotionProject? = null
+
+ composeTestRule.setContent {
+ ProjectDetailsScreen(
+ project = project,
+ onBackClick = {},
+ onEditClick = { editedProject = it },
+ onShareClick = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText("Edit Project").performClick()
+
+ assertEquals(project.id, editedProject?.id)
+ }
+}
diff --git a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerTest.kt b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerTest.kt
index 17e04c98..3de95a5f 100644
--- a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerTest.kt
+++ b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerTest.kt
@@ -24,7 +24,6 @@ import java.util.UUID
*/
@RunWith(AndroidJUnit4::class)
class LyricsMotionWorkerTest {
-
private val context: Context = ApplicationProvider.getApplicationContext()
private val songName = "Worker Test Song"
private val projectId = songName.md5()
@@ -161,4 +160,4 @@ class LyricsMotionWorkerTest {
WorkManager.getInstance(context).cancelWorkById(workId1)
WorkManager.getInstance(context).cancelWorkById(workId2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientText.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientText.kt
index c9b6e502..42cbd30e 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientText.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientText.kt
@@ -26,6 +26,7 @@ import com.tejpratapsingh.lyricsmaker.presentation.ui.theme.ThemeBlue
import com.tejpratapsingh.lyricsmaker.presentation.ui.theme.ThemePink
@Composable
+@Suppress("FunctionName")
fun GradientText(
text: String,
modifier: Modifier = Modifier,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientTextPreview.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientTextPreview.kt
index ec2d1983..fd046b6c 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientTextPreview.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/common/GradientTextPreview.kt
@@ -5,6 +5,7 @@ import androidx.compose.ui.tooling.preview.Preview
@Preview(showBackground = true)
@Composable
+@Suppress("ktlint:standard:function-naming")
fun GradientTextPreview() {
GradientText(text = "Lyrics Maker")
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/details/ProjectDetailsScreen.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/details/ProjectDetailsScreen.kt
index aa91ddd9..9bd06376 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/details/ProjectDetailsScreen.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/details/ProjectDetailsScreen.kt
@@ -48,6 +48,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
+@Suppress("ktlint:standard:function-naming")
fun ProjectDetailsScreen(
project: MotionProject,
onBackClick: () -> Unit,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/DragHandle.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/DragHandle.kt
index 1d4a5147..811ff0f4 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/DragHandle.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/DragHandle.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.zIndex
import kotlin.math.roundToInt
@Composable
+@Suppress("FunctionName")
internal fun DragHandle(
label: String,
color: Color,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/LyricRow.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/LyricRow.kt
index 342f5683..6a0212ec 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/LyricRow.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/LyricRow.kt
@@ -20,6 +20,7 @@ import com.tejpratapsingh.lyricsmaker.data.lrc.SyncedLyricFrame
import com.tejpratapsingh.motionlib.core.provideCurrentConfig
@Composable
+@Suppress("FunctionName")
internal fun LyricRow(
line: SyncedLyricFrame,
isSelected: Boolean,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/StartDragHandle.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/StartDragHandle.kt
index 93fbecdb..3421a4e7 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/StartDragHandle.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/StartDragHandle.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.zIndex
import kotlin.math.roundToInt
@Composable
+@Suppress("FunctionName")
internal fun StartDragHandle(
color: Color,
autoScroll: AutoScrollState,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/SyncedLyricsSelector.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/SyncedLyricsSelector.kt
index feeeab24..e349cc99 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/SyncedLyricsSelector.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/lyrics/SyncedLyricsSelector.kt
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
@@ -19,7 +18,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
-import androidx.compose.material.icons.filled.DragHandle
import androidx.compose.material.icons.filled.NavigateNext
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.HorizontalDivider
@@ -53,12 +51,13 @@ import com.tejpratapsingh.lyricsmaker.presentation.ui.theme.ThemeBlue
import com.tejpratapsingh.lyricsmaker.presentation.ui.theme.ThemePink
import com.tejpratapsingh.lyricsmaker.presentation.viewmodel.LyricsViewModel
import com.tejpratapsingh.motionlib.core.provideCurrentConfig
-import kotlin.math.max
-import kotlin.math.min
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
+import kotlin.math.max
+import kotlin.math.min
@Composable
+@Suppress("FunctionName")
fun SyncedLyricsSelector(
viewModel: LyricsViewModel,
modifier: Modifier = Modifier,
@@ -112,7 +111,8 @@ fun SyncedLyricsSelector(
when (activeHandle) {
ListItem.StartHandle -> {
- val found = findLyricIndexAt(listState, autoScroll.listTopInRoot, livePointerYInRoot)
+ val found =
+ findLyricIndexAt(listState, autoScroll.listTopInRoot, livePointerYInRoot)
if (found != null) {
if (moveMode) {
val newStart = found.coerceIn(0, lastIdx - rangeSize)
@@ -127,7 +127,8 @@ fun SyncedLyricsSelector(
}
ListItem.EndHandle -> {
- val found = findLyricIndexAt(listState, autoScroll.listTopInRoot, livePointerYInRoot)
+ val found =
+ findLyricIndexAt(listState, autoScroll.listTopInRoot, livePointerYInRoot)
if (found != null) {
if (moveMode) {
val newEnd = found.coerceIn(rangeSize, lastIdx)
@@ -153,7 +154,10 @@ fun SyncedLyricsSelector(
if (lyrics.isEmpty()) {
emptyList()
} else {
- lyrics.subList(selection.minIndex, (selection.maxIndex + 1).coerceAtMost(lyrics.size))
+ lyrics.subList(
+ selection.minIndex,
+ (selection.maxIndex + 1).coerceAtMost(lyrics.size),
+ )
}
}
}
@@ -202,7 +206,7 @@ fun SyncedLyricsSelector(
if (lyrics.isNotEmpty()) {
Surface(
tonalElevation = 2.dp,
- modifier = Modifier.statusBarsPadding()
+ modifier = Modifier.statusBarsPadding(),
) {
Row(
modifier =
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/navigation/AppNavHost.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/navigation/AppNavHost.kt
index 34530504..9ba6a89d 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/navigation/AppNavHost.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/navigation/AppNavHost.kt
@@ -26,6 +26,7 @@ import com.tejpratapsingh.motionlib.templates.sdui.MotionTemplateSDUIProvider
import com.tejpratapsingh.motionstore.tables.MotionProject
@Composable
+@Suppress("ktlint:standard:function-naming")
fun AppNavHost(
navController: NavHostController,
currentScreen: Screen = Screen.Projects,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/CreateNewProjectCard.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/CreateNewProjectCard.kt
index 628b4ad2..d382349c 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/CreateNewProjectCard.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/CreateNewProjectCard.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable
+@Suppress("ktlint:standard:function-naming")
internal fun CreateNewProjectCard(
onClick: () -> Unit,
modifier: Modifier = Modifier,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/DeleteConfirmationDialog.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/DeleteConfirmationDialog.kt
index 66d3b416..e2c0495d 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/DeleteConfirmationDialog.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/DeleteConfirmationDialog.kt
@@ -12,6 +12,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
+@Suppress("ktlint:standard:function-naming")
internal fun DeleteConfirmationDialog(
projectName: String,
onConfirm: () -> Unit,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectCard.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectCard.kt
index fff37641..6ce95454 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectCard.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectCard.kt
@@ -47,6 +47,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
+@Suppress("ktlint:standard:function-naming")
internal fun ProjectCard(
project: MotionProject,
onClick: () -> Unit,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectThumbnailExtractor.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectThumbnailExtractor.kt
index acf2db91..9f09cb6d 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectThumbnailExtractor.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectThumbnailExtractor.kt
@@ -2,7 +2,6 @@ package com.tejpratapsingh.lyricsmaker.presentation.compose.projects
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
-import java.lang.Exception
fun extractFirstFrame(videoPath: String): Bitmap? {
val retriever = MediaMetadataRetriever()
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsRoute.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsRoute.kt
index 180382c5..f46c282b 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsRoute.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsRoute.kt
@@ -9,6 +9,7 @@ import com.tejpratapsingh.lyricsmaker.presentation.viewmodel.ProjectsViewModel
import com.tejpratapsingh.motionstore.tables.MotionProject
@Composable
+@Suppress("ktlint:standard:function-naming")
fun ProjectsRoute(
viewModel: ProjectsViewModel,
onCreateNew: () -> Unit,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsScreen.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsScreen.kt
index 8035b181..122779f9 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsScreen.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ProjectsScreen.kt
@@ -42,6 +42,7 @@ import com.tejpratapsingh.motionstore.dao.MotionProjectDao
import com.tejpratapsingh.motionstore.tables.MotionProject
@Composable
+@Suppress("ktlint:standard:function-naming")
fun ProjectsScreen(
projects: List,
isRefreshing: Boolean,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ThumbnailCache.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ThumbnailCache.kt
index 7f248b7e..6c6f6a02 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ThumbnailCache.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/projects/ThumbnailCache.kt
@@ -2,21 +2,25 @@ package com.tejpratapsingh.lyricsmaker.presentation.compose.projects
import android.graphics.Bitmap
import android.util.LruCache
-import java.util.*
object ThumbnailCache {
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
private val cacheSize = maxMemory / 8 // Use 1/8th of available memory
- private val cache = object : LruCache(cacheSize) {
- override fun sizeOf(key: String, value: Bitmap): Int {
- return value.byteCount / 1024
+ private val cache =
+ object : LruCache(cacheSize) {
+ override fun sizeOf(
+ key: String,
+ value: Bitmap,
+ ): Int = value.byteCount / 1024
}
- }
fun get(key: String): Bitmap? = cache.get(key)
- fun put(key: String, bitmap: Bitmap) {
+ fun put(
+ key: String,
+ bitmap: Bitmap,
+ ) {
cache.put(key, bitmap)
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/search/SearchScreen.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/search/SearchScreen.kt
index ac272f51..087787d9 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/search/SearchScreen.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/search/SearchScreen.kt
@@ -1,34 +1,24 @@
package com.tejpratapsingh.lyricsmaker.presentation.compose.search
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.History
-import androidx.compose.material.icons.rounded.MusicNote
-import androidx.compose.material.icons.rounded.Timer
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
-import androidx.compose.material3.ListItem
-import androidx.compose.material3.ListItemDefaults
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
@@ -36,53 +26,37 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
-import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalSoftwareKeyboardController
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.tejpratapsingh.lyricsmaker.data.api.lrclib.model.LyricsResponse
-import com.tejpratapsingh.lyricsmaker.data.store.RecentSearchHelper
import com.tejpratapsingh.lyricsmaker.presentation.viewmodel.LyricsUiState
import com.tejpratapsingh.lyricsmaker.presentation.viewmodel.LyricsViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
+@Suppress("FunctionName")
fun SearchScreen(
- modifier: Modifier = Modifier,
viewModel: LyricsViewModel,
+ modifier: Modifier = Modifier,
+ onBack: () -> Unit = {},
onLyricsSelected: (LyricsResponse) -> Unit = {},
) {
- val context = LocalContext.current
- val query = viewModel.query.collectAsState()
- val uiState = viewModel.uiState.collectAsState()
- val recentSearches = remember { mutableStateOf(RecentSearchHelper.getSearches(context)) }
-
- val keyboardController = LocalSoftwareKeyboardController.current
+ val uiState by viewModel.uiState.collectAsState()
+ val query by viewModel.query.collectAsState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState())
var headerHeightPx by remember { mutableFloatStateOf(0f) }
- LaunchedEffect(headerHeightPx) {
- scrollBehavior.state.heightOffsetLimit = -headerHeightPx
- }
-
Box(
modifier =
modifier
@@ -99,10 +73,9 @@ fun SearchScreen(
x = 0,
y = (headerHeightPx + scrollBehavior.state.heightOffset).toInt(),
)
- }
- .padding(horizontal = 16.dp),
+ }.padding(horizontal = 16.dp),
) {
- when (val state = uiState.value) {
+ when (val state = uiState) {
is LyricsUiState.Success -> {
LazyColumn(
modifier = Modifier.fillMaxSize(),
@@ -113,105 +86,47 @@ fun SearchScreen(
Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
- colors =
- CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
- ),
) {
- state.lyrics.forEachIndexed { index, lyrics ->
- ListItem(
- headlineContent = {
- Text(
- text = "${lyrics.trackName} - ${lyrics.artistName}",
- style = MaterialTheme.typography.titleMedium,
- fontWeight = FontWeight.Bold,
- )
- },
- supportingContent = {
- Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
- Row(verticalAlignment = Alignment.CenterVertically) {
- Icon(
- imageVector = Icons.Rounded.Timer,
- contentDescription = null,
- modifier = Modifier.size(16.dp),
- tint = MaterialTheme.colorScheme.onSurfaceVariant,
- )
- Spacer(Modifier.width(4.dp))
- Text(
- text = lyrics.getReadableDuration(),
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- Text(
- text = lyrics.getLyrics(),
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodySmall,
- )
- }
- },
- leadingContent = {
- Icon(
- imageVector = Icons.Rounded.MusicNote,
- contentDescription = null,
- modifier = Modifier.size(24.dp),
- )
- },
- colors = ListItemDefaults.colors(containerColor = Color.Transparent),
- modifier = Modifier.clickable { onLyricsSelected(lyrics) },
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(
+ "Found ${state.lyrics.size} results",
+ style = MaterialTheme.typography.labelLarge,
+ color = MaterialTheme.colorScheme.primary,
)
- if (index < state.lyrics.size - 1) {
- HorizontalDivider(
- modifier = Modifier.padding(horizontal = 16.dp),
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f),
- )
- }
}
}
}
- }
- }
- else -> {
- LazyColumn(
- modifier = Modifier.fillMaxSize(),
- ) {
- item {
- Spacer(modifier = Modifier.height(16.dp))
- if (recentSearches.value.isNotEmpty()) {
- Text("Recent Searches:", style = MaterialTheme.typography.titleMedium)
- Spacer(modifier = Modifier.height(8.dp))
- Card(
- modifier = Modifier.fillMaxWidth(),
- colors =
- CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
- ),
+ items(state.lyrics) { lyric ->
+ Card(
+ onClick = {
+ viewModel.selectedLyric.value = lyric
+ onLyricsSelected(lyric)
+ },
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp),
+ ) {
+ Row(
+ modifier = Modifier.padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
- recentSearches.value.forEachIndexed { index, search ->
- ListItem(
- headlineContent = { Text(search) },
- leadingContent = {
- Icon(
- imageVector = Icons.Rounded.History,
- contentDescription = null,
- modifier = Modifier.size(20.dp),
- )
- },
- colors = ListItemDefaults.colors(containerColor = Color.Transparent),
- modifier =
- Modifier.clickable {
- viewModel.query.tryEmit(search)
- keyboardController?.hide()
- viewModel.searchLyrics(search)
- },
+ Column(modifier = Modifier.weight(1f)) {
+ Text(
+ text = lyric.trackName ?: "",
+ style = MaterialTheme.typography.titleMedium,
)
- if (index < recentSearches.value.size - 1) {
- HorizontalDivider(
- modifier = Modifier.padding(horizontal = 16.dp),
- thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f),
+ Text(
+ text = lyric.artistName ?: "",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ if (!lyric.albumName.isNullOrEmpty()) {
+ Text(
+ text = lyric.albumName!!,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
@@ -220,6 +135,43 @@ fun SearchScreen(
}
}
}
+
+ is LyricsUiState.Error -> {
+ Box(modifier = Modifier.fillMaxSize()) {
+ Text(
+ text = state.message,
+ color = MaterialTheme.colorScheme.error,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+
+ is LyricsUiState.Loading -> {
+ Box(modifier = Modifier.fillMaxSize()) {
+ CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
+ }
+ }
+
+ else -> {
+ Box(modifier = Modifier.fillMaxSize()) {
+ Column(
+ modifier = Modifier.align(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ Icons.Default.Search,
+ contentDescription = null,
+ modifier = Modifier.size(64.dp),
+ tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.3f),
+ )
+ Text(
+ "Enter a song or artist name to search",
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
+ }
}
}
@@ -230,14 +182,12 @@ fun SearchScreen(
.fillMaxWidth()
.onGloballyPositioned {
headerHeightPx = it.size.height.toFloat()
- }
- .offset {
+ }.offset {
IntOffset(
x = 0,
y = scrollBehavior.state.heightOffset.toInt(),
)
- }
- .statusBarsPadding()
+ }.statusBarsPadding()
.zIndex(1f),
color = MaterialTheme.colorScheme.surface,
) {
@@ -247,38 +197,27 @@ fun SearchScreen(
Text(
text = "Search Lyrics",
style = MaterialTheme.typography.headlineLarge,
- modifier =
- Modifier
- .align(CenterHorizontally)
- .padding(16.dp),
+ modifier = Modifier.padding(bottom = 16.dp),
)
+
OutlinedTextField(
- value = query.value,
- onValueChange = { viewModel.query.tryEmit(it) },
- label = { Text("Search") },
- singleLine = true,
+ value = query,
+ onValueChange = { viewModel.query.value = it },
+ modifier = Modifier.fillMaxWidth(),
+ placeholder = { Text("Search songs, artists...") },
+ leadingIcon = {
+ IconButton(onClick = onBack) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ }
+ },
trailingIcon = {
- if (uiState.value is LyricsUiState.Loading) {
- CircularProgressIndicator(
- modifier = Modifier.size(20.dp),
- strokeWidth = 2.dp,
- )
+ if (query.isNotEmpty()) {
+ IconButton(onClick = { viewModel.query.value = "" }) {
+ Icon(Icons.Default.Search, contentDescription = "Clear")
+ }
}
},
- keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Search),
- keyboardActions =
- KeyboardActions(
- onSearch = {
- val searchQuery = query.value.trim()
- if (searchQuery.isNotBlank()) {
- keyboardController?.hide()
- RecentSearchHelper.saveSearch(context, searchQuery)
- recentSearches.value = RecentSearchHelper.getSearches(context)
- viewModel.searchLyrics(query = searchQuery)
- }
- },
- ),
- modifier = Modifier.fillMaxWidth(),
+ singleLine = true,
)
}
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/LyricsTemplateSelector.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/LyricsTemplateSelector.kt
index f1904b36..952c06e2 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/LyricsTemplateSelector.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/LyricsTemplateSelector.kt
@@ -2,6 +2,7 @@ package com.tejpratapsingh.lyricsmaker.presentation.compose.templates
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -11,7 +12,6 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerDefaults
import androidx.compose.foundation.pager.rememberPagerState
@@ -36,6 +36,7 @@ import com.tejpratapsingh.motionlib.templates.model.MotionTemplate
import com.tejpratapsingh.motionstore.tables.MotionProject
@Composable
+@Suppress("ktlint:standard:function-naming")
fun LyricsTemplateSelector(
project: MotionProject,
onBack: () -> Unit,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/TemplatePreviewItem.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/TemplatePreviewItem.kt
index 8024d1fc..d30dd5bf 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/TemplatePreviewItem.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/templates/TemplatePreviewItem.kt
@@ -14,8 +14,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -28,6 +28,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
+@Suppress("ktlint:standard:function-naming")
internal fun TemplatePreviewItem(
project: MotionProject,
template: MotionTemplate,
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/templates/ZoomLyricsTemplate.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/templates/ZoomLyricsTemplate.kt
index e7e38326..8f0251d0 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/templates/ZoomLyricsTemplate.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/templates/ZoomLyricsTemplate.kt
@@ -3,7 +3,6 @@ package com.tejpratapsingh.lyricsmaker.presentation.templates
import android.view.Gravity
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.net.toUri
-import androidx.core.view.isVisible
import com.tejpratapsingh.lyricsmaker.data.lrc.SyncedLyricFrame
import com.tejpratapsingh.motionlib.core.MotionTextVariant
import com.tejpratapsingh.motionlib.core.motion.transitions.CrossFadeTransition
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Theme.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Theme.kt
index 6aa53c61..7fbba2fd 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Theme.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Theme.kt
@@ -34,6 +34,7 @@ private val LightColorScheme =
)
@Composable
+@Suppress("ktlint:standard:function-naming")
fun AnimatorTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModel.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModel.kt
index 0ef06168..bcc9b05a 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModel.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModel.kt
@@ -104,11 +104,12 @@ class LyricsViewModel : ViewModel() {
var selectedLyrics: List = emptyList()
set(value) {
field = value
- selectedStartTimeInSeconds = if (value.isNotEmpty()) {
- value.first().frame.toFloat() / provideCurrentConfig().fps
- } else {
- 0f
- }
+ selectedStartTimeInSeconds =
+ if (value.isNotEmpty()) {
+ value.first().frame.toFloat() / provideCurrentConfig().fps
+ } else {
+ 0f
+ }
}
get() {
if (field.isEmpty()) return emptyList()
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerCancelReceiver.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerCancelReceiver.kt
index eff6eea4..694e3069 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerCancelReceiver.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorkerCancelReceiver.kt
@@ -12,7 +12,10 @@ class LyricsMotionWorkerCancelReceiver : BroadcastReceiver() {
const val EXTRA_WORK_ID = "extra_work_id"
}
- override fun onReceive(context: Context, intent: Intent) {
+ override fun onReceive(
+ context: Context,
+ intent: Intent,
+ ) {
if (intent.action == ACTION_CANCEL) {
val workIdString = intent.getStringExtra(EXTRA_WORK_ID)
if (workIdString != null) {
diff --git a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ScreenTest.kt b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ScreenTest.kt
index dccb3535..12077039 100644
--- a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ScreenTest.kt
+++ b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/compose/ScreenTest.kt
@@ -11,7 +11,6 @@ import org.junit.Test
* helper introduced in this PR.
*/
class ScreenTest {
-
// ------------------------------------------------------------------
// Screen.ProjectDetails.route template
// ------------------------------------------------------------------
@@ -99,4 +98,4 @@ class ScreenTest {
fun `Lyrics screen has route 'lyrics'`() {
assertEquals("lyrics", Screen.Lyrics.route)
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModelTest.kt b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModelTest.kt
index 9a740416..1c366bcb 100644
--- a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModelTest.kt
+++ b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/presentation/viewmodel/LyricsViewModelTest.kt
@@ -13,7 +13,6 @@ import org.junit.Test
* and the new selectedStartTimeInSeconds field introduced in this PR.
*/
class LyricsViewModelTest {
-
private lateinit var viewModel: LyricsViewModel
@Before
@@ -40,10 +39,11 @@ class LyricsViewModelTest {
@Test
fun `selectedLyrics getter normalises frames by subtracting the first frame`() {
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = 48, text = "Hello"),
- SyncedLyricFrame(frame = 96, text = "World"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = 48, text = "Hello"),
+ SyncedLyricFrame(frame = 96, text = "World"),
+ )
val result = viewModel.selectedLyrics
@@ -56,10 +56,11 @@ class LyricsViewModelTest {
@Test
fun `selectedLyrics getter preserves texts after frame normalisation`() {
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = 10, text = "Line A"),
- SyncedLyricFrame(frame = 20, text = "Line B"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = 10, text = "Line A"),
+ SyncedLyricFrame(frame = 20, text = "Line B"),
+ )
val result = viewModel.selectedLyrics
@@ -70,10 +71,11 @@ class LyricsViewModelTest {
@Test
fun `selectedLyrics getter sorts frames in ascending order`() {
// Provide frames out of order to verify sorting.
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = 100, text = "Second"),
- SyncedLyricFrame(frame = 50, text = "First"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = 100, text = "Second"),
+ SyncedLyricFrame(frame = 50, text = "First"),
+ )
val result = viewModel.selectedLyrics
@@ -87,9 +89,10 @@ class LyricsViewModelTest {
@Test
fun `selectedLyrics getter returns single item with frame normalised to zero`() {
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = 72, text = "Only line"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = 72, text = "Only line"),
+ )
val result = viewModel.selectedLyrics
@@ -112,10 +115,11 @@ class LyricsViewModelTest {
@Test
fun `selectedStartTimeInSeconds is computed from first frame divided by fps`() {
// fps = 24 (set in setUp), first frame = 48 → expected = 48 / 24 = 2.0 seconds
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = 48, text = "Start"),
- SyncedLyricFrame(frame = 72, text = "End"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = 48, text = "Start"),
+ SyncedLyricFrame(frame = 72, text = "End"),
+ )
assertEquals(2.0f, viewModel.selectedStartTimeInSeconds, 0.001f)
}
@@ -146,12 +150,13 @@ class LyricsViewModelTest {
// the raw first element — NOT the minimum frame. Verify this contract.
val fps = 24
val firstFrame = 72 // This will be value.first()
- viewModel.selectedLyrics = listOf(
- SyncedLyricFrame(frame = firstFrame, text = "C"),
- SyncedLyricFrame(frame = 24, text = "A"),
- )
+ viewModel.selectedLyrics =
+ listOf(
+ SyncedLyricFrame(frame = firstFrame, text = "C"),
+ SyncedLyricFrame(frame = 24, text = "A"),
+ )
val expected = firstFrame.toFloat() / fps
assertEquals(expected, viewModel.selectedStartTimeInSeconds, 0.001f)
}
-}
\ No newline at end of file
+}
diff --git a/modules/media3-motion-ext/src/main/AndroidManifest.xml b/modules/media3-motion-ext/src/main/AndroidManifest.xml
index 94cbbcfc..cc947c56 100644
--- a/modules/media3-motion-ext/src/main/AndroidManifest.xml
+++ b/modules/media3-motion-ext/src/main/AndroidManifest.xml
@@ -1 +1 @@
-
+
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/FrameProcessor.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/FrameProcessor.kt
index 7eb7960e..2282f1fb 100644
--- a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/FrameProcessor.kt
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/FrameProcessor.kt
@@ -14,7 +14,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import timber.log.Timber
-import java.io.File
import java.util.Locale
import java.util.concurrent.atomic.AtomicInteger
@@ -22,7 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger
* Handles frame generation and caching for video production.
*/
class FrameProcessor(
- private val subDirName: String
+ private val subDirName: String,
) {
/**
* Renders frames from [motionComposerView] and saves them to the cache directory.
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3CompositionBuilder.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3CompositionBuilder.kt
index 024ae9f6..e7ed7512 100644
--- a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3CompositionBuilder.kt
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3CompositionBuilder.kt
@@ -17,7 +17,6 @@ import kotlin.math.roundToLong
*/
@UnstableApi
class Media3CompositionBuilder {
-
/**
* Builds a [Composition] from the frames in [frameDirectory] and the provided [motionAudio].
*/
@@ -95,9 +94,13 @@ class Media3CompositionBuilder {
}
}
- private fun frameToMs(frame: Int, fps: Int): Long =
- ((frame.toDouble() / fps.toDouble()) * 1_000).roundToLong()
+ private fun frameToMs(
+ frame: Int,
+ fps: Int,
+ ): Long = ((frame.toDouble() / fps.toDouble()) * 1_000).roundToLong()
- private fun frameToUs(frame: Int, fps: Int): Long =
- ((frame.toDouble() / fps.toDouble()) * 1_000_000).roundToLong()
+ private fun frameToUs(
+ frame: Int,
+ fps: Int,
+ ): Long = ((frame.toDouble() / fps.toDouble()) * 1_000_000).roundToLong()
}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3TransformerRunner.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3TransformerRunner.kt
index 8fc9fb70..6c3ed9a0 100644
--- a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3TransformerRunner.kt
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3TransformerRunner.kt
@@ -22,7 +22,6 @@ import kotlin.coroutines.resumeWithException
*/
@UnstableApi
class Media3TransformerRunner {
-
/**
* Exports the [composition] to [outputFile] using Media3 [Transformer].
*/
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3VideoProducerAdapter.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3VideoProducerAdapter.kt
index 847ded8e..7353c4ea 100644
--- a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3VideoProducerAdapter.kt
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3VideoProducerAdapter.kt
@@ -21,7 +21,6 @@ class Media3VideoProducerAdapter(
private val compositionBuilder: Media3CompositionBuilder = Media3CompositionBuilder(),
private val transformerRunner: Media3TransformerRunner = Media3TransformerRunner(),
) : VideoProducerAdapter {
-
private val subDirName by lazy { UUID.randomUUID().toString() }
override suspend fun produceVideo(
@@ -56,17 +55,18 @@ class Media3VideoProducerAdapter(
)
// S: Composition building responsibility delegated to Media3CompositionBuilder
- val composition = compositionBuilder.build(
- frameDirectory = subDir,
- motionAudio = motionAudio,
- motionConfig = motionConfig
- )
+ val composition =
+ compositionBuilder.build(
+ frameDirectory = subDir,
+ motionAudio = motionAudio,
+ motionConfig = motionConfig,
+ )
// S: Transformer execution responsibility delegated to Media3TransformerRunner
transformerRunner.export(
context = context,
composition = composition,
- outputFile = outputFile
+ outputFile = outputFile,
)
} finally {
if (subDir.exists()) {
diff --git a/modules/ml-kit-ext/src/main/java/com/tejpratapsingh/motionlib/mlkit/plugins/SubjectSegmentationPlugin.kt b/modules/ml-kit-ext/src/main/java/com/tejpratapsingh/motionlib/mlkit/plugins/SubjectSegmentationPlugin.kt
index d58fd280..01da765e 100644
--- a/modules/ml-kit-ext/src/main/java/com/tejpratapsingh/motionlib/mlkit/plugins/SubjectSegmentationPlugin.kt
+++ b/modules/ml-kit-ext/src/main/java/com/tejpratapsingh/motionlib/mlkit/plugins/SubjectSegmentationPlugin.kt
@@ -14,9 +14,9 @@ import java.nio.FloatBuffer
* This processes the bitmap directly and returns a new bitmap with the background removed.
*/
class SubjectSegmentationPlugin : MotionPlugin {
-
private val options =
- SubjectSegmenterOptions.Builder()
+ SubjectSegmenterOptions
+ .Builder()
.enableForegroundConfidenceMask()
.build()
@@ -40,27 +40,28 @@ class SubjectSegmentationPlugin : MotionPlugin {
private fun applyMaskToBitmap(
source: Bitmap,
- mask: FloatBuffer
+ mask: FloatBuffer,
): Bitmap {
val width = source.width
val height = source.height
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
-
+
mask.rewind()
val pixels = IntArray(width * height)
source.getPixels(pixels, 0, width, 0, 0, width, height)
-
+
for (i in 0 until width * height) {
val confidence = if (mask.hasRemaining()) mask.get() else 0.0f
val alpha = (Color.alpha(pixels[i]) * confidence).toInt()
- pixels[i] = Color.argb(
- alpha,
- Color.red(pixels[i]),
- Color.green(pixels[i]),
- Color.blue(pixels[i])
- )
+ pixels[i] =
+ Color.argb(
+ alpha,
+ Color.red(pixels[i]),
+ Color.green(pixels[i]),
+ Color.blue(pixels[i]),
+ )
}
-
+
result.setPixels(pixels, 0, width, 0, 0, width, height)
return result
}
diff --git a/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/dao/MotionProjectDao.kt b/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/dao/MotionProjectDao.kt
index a5e75af8..fd28f6bf 100644
--- a/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/dao/MotionProjectDao.kt
+++ b/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/dao/MotionProjectDao.kt
@@ -87,11 +87,10 @@ class MotionProjectDao(
),
)
- private fun String.toJsonObject(): JsonObject {
- return try {
+ private fun String.toJsonObject(): JsonObject =
+ try {
JsonParser.parseString(this).asJsonObject
} catch (e: Exception) {
JsonObject()
}
- }
}
diff --git a/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/infra/PreferenceManager.kt b/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/infra/PreferenceManager.kt
index 2e1e3585..db0f0f4b 100644
--- a/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/infra/PreferenceManager.kt
+++ b/modules/motion-store/src/main/java/com/tejpratapsingh/motionstore/infra/PreferenceManager.kt
@@ -4,7 +4,9 @@ import android.content.Context
import android.content.SharedPreferences
import com.tejpratapsingh.motionstore.dao.MotionProjectDao
-class PreferenceManager(context: Context) {
+class PreferenceManager(
+ context: Context,
+) {
private val prefs: SharedPreferences =
context.getSharedPreferences("lyrics_maker_prefs", Context.MODE_PRIVATE)
diff --git a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/TimelineData.kt b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/TimelineData.kt
index 0ee93357..26b6b781 100644
--- a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/TimelineData.kt
+++ b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/TimelineData.kt
@@ -2,7 +2,7 @@ package com.tejpratapsingh.motioneditor
data class TimelineTrack(
val id: String,
- val items: List
+ val items: List,
)
data class TimelineItem(
@@ -10,5 +10,5 @@ data class TimelineItem(
val type: String,
val startFrame: Int,
val endFrame: Int,
- val label: String
+ val label: String,
)
diff --git a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionEditorScreen.kt b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionEditorScreen.kt
index a9ec2199..205d2b72 100644
--- a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionEditorScreen.kt
+++ b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionEditorScreen.kt
@@ -1,7 +1,9 @@
package com.tejpratapsingh.motioneditor.ui
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -9,6 +11,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
@@ -20,14 +23,11 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.foundation.gestures.detectDragGestures
-import androidx.compose.foundation.layout.BoxWithConstraints
-import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@@ -42,6 +42,7 @@ import com.tejpratapsingh.motionstore.tables.MotionProject
import com.tejpratapsingh.motionstore.tables.SyncTracker
@Composable
+@Suppress("ktlint:standard:function-naming")
fun MotionEditorScreen(
project: MotionProject,
onBackClick: () -> Unit,
@@ -180,6 +181,7 @@ fun MotionEditorScreen(
@Preview(showBackground = true)
@Composable
+@Suppress("ktlint:standard:function-naming")
fun PreviewMotionEditorScreen() {
val sampleProject =
MotionProject(
diff --git a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionTimeline.kt b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionTimeline.kt
index a01ed90d..f3d445ac 100644
--- a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionTimeline.kt
+++ b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/ui/MotionTimeline.kt
@@ -15,10 +15,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -32,23 +35,21 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.Surface
-import androidx.compose.ui.text.style.TextOverflow
import com.tejpratapsingh.motioneditor.TimelineItem
import com.tejpratapsingh.motioneditor.TimelineTrack
-import androidx.compose.ui.input.pointer.pointerInput
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@Composable
+@Suppress("ktlint:standard:function-naming")
fun MotionTimeline(
tracks: List,
currentFrame: Int,
@@ -116,8 +117,7 @@ fun MotionTimeline(
change.consume()
onResize(dragAmount)
}
- }
- .horizontalScroll(horizontalScrollState),
+ }.horizontalScroll(horizontalScrollState),
) {
Row {
Spacer(modifier = Modifier.width(halfWidth))
@@ -168,6 +168,7 @@ fun MotionTimeline(
}
@Composable
+@Suppress("ktlint:standard:function-naming")
fun TimeScaleView(
totalFrames: Int,
fps: Int,
@@ -229,6 +230,7 @@ private fun formatFrameToTime(
}
@Composable
+@Suppress("ktlint:standard:function-naming")
fun TimelineTrackView(
track: TimelineTrack,
pixelsPerFrame: Float,
@@ -249,6 +251,7 @@ fun TimelineTrackView(
}
@Composable
+@Suppress("ktlint:standard:function-naming")
fun TimelineItemView(
item: TimelineItem,
pixelsPerFrame: Float,
@@ -276,7 +279,11 @@ fun TimelineItemView(
) {
Box(contentAlignment = Alignment.Center) {
Text(
- text = item.label.firstOrNull()?.toString()?.uppercase() ?: "?",
+ text =
+ item.label
+ .firstOrNull()
+ ?.toString()
+ ?.uppercase() ?: "?",
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.labelLarge,
)
@@ -306,6 +313,7 @@ fun TimelineItemView(
@Preview(showBackground = true)
@Composable
+@Suppress("ktlint:standard:function-naming")
fun PreviewMotionTimeline() {
val sampleTracks =
listOf(
diff --git a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/utils/TimelineUtils.kt b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/utils/TimelineUtils.kt
index 06b86cff..26e66396 100644
--- a/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/utils/TimelineUtils.kt
+++ b/modules/motion-video-editor/src/main/java/com/tejpratapsingh/motioneditor/utils/TimelineUtils.kt
@@ -8,7 +8,10 @@ import com.tejpratapsingh.motioneditor.TimelineTrack
import com.tejpratapsingh.motionlib.core.MotionView
object TimelineUtils {
- fun fromSdui(context: Context, sduiJson: JsonObject): List {
+ fun fromSdui(
+ context: Context,
+ sduiJson: JsonObject,
+ ): List {
val views = sduiJson.getMotionViews(context)
return fromMotionViews(views)
}
@@ -19,15 +22,16 @@ object TimelineUtils {
return views.mapIndexed { index, view ->
TimelineTrack(
id = "track_$index",
- items = listOf(
- TimelineItem(
- id = view.hashCode().toString(),
- type = view.javaClass.simpleName,
- startFrame = view.startFrame,
- endFrame = view.endFrame,
- label = view.javaClass.simpleName
- )
- )
+ items =
+ listOf(
+ TimelineItem(
+ id = view.hashCode().toString(),
+ type = view.javaClass.simpleName,
+ startFrame = view.startFrame,
+ endFrame = view.endFrame,
+ label = view.javaClass.simpleName,
+ ),
+ ),
)
}
}
diff --git a/modules/motion-video-player/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/MotionVideoPlayerCompose.kt b/modules/motion-video-player/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/MotionVideoPlayerCompose.kt
index 285f9c2e..7b91ca5f 100644
--- a/modules/motion-video-player/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/MotionVideoPlayerCompose.kt
+++ b/modules/motion-video-player/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/MotionVideoPlayerCompose.kt
@@ -47,6 +47,7 @@ import kotlinx.coroutines.delay
import java.util.Locale
@Composable
+@Suppress("FunctionName")
fun MotionVideoPlayerCompose(
motionVideoProducer: MotionVideoProducer,
modifier: Modifier = Modifier,
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Spring.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Spring.kt
index 7719d621..227a9d1f 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Spring.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Spring.kt
@@ -99,9 +99,9 @@ object Spring {
// numerically degenerate; fallback
1.0 - exp(-omega0 * t)
} else {
- val A = (v0 - r2 * (-1.0)) / (r1 - r2) // solving linear system
- val B = -1.0 - A
- 1.0 + A * exp(r1 * t) + B * exp(r2 * t)
+ val a = (v0 - r2 * (-1.0)) / (r1 - r2) // solving linear system
+ val b = -1.0 - a
+ 1.0 + a * exp(r1 * t) + b * exp(r2 * t)
}
}
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionVideoProducer.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionVideoProducer.kt
index 3b398a38..9af4be07 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionVideoProducer.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionVideoProducer.kt
@@ -51,7 +51,7 @@ open class MotionVideoProducer private constructor(
override fun addTransition(
transition: MotionTransition,
- duration: Int
+ duration: Int,
): MotionVideoProducer {
pendingTransition = Pair(transition, duration)
return this
@@ -66,31 +66,33 @@ open class MotionVideoProducer private constructor(
transition.apply(currentLastView, motionView, duration)
pendingTransition = null
}
-
+
lastMotionView = motionView
totalFrames = maxOf(totalFrames, motionView.endFrame)
motionComposerView.apply {
val layoutInfo = motionView.layoutInfo
motionView.layoutBy(
- x = if (layoutInfo.gravity and android.view.Gravity.CENTER_HORIZONTAL == android.view.Gravity.CENTER_HORIZONTAL) {
- centerHorizontallyTo { parent.centerX() }
- } else if (layoutInfo.gravity and android.view.Gravity.LEFT == android.view.Gravity.LEFT) {
- leftTo { parent.left() + layoutInfo.margin.left.toXInt() }
- } else if (layoutInfo.gravity and android.view.Gravity.RIGHT == android.view.Gravity.RIGHT) {
- rightTo { parent.right() - layoutInfo.margin.right.toXInt() }
- } else {
- centerHorizontallyTo { parent.centerX() }
- },
- y = if (layoutInfo.gravity and android.view.Gravity.CENTER_VERTICAL == android.view.Gravity.CENTER_VERTICAL) {
- centerVerticallyTo { parent.centerY() }
- } else if (layoutInfo.gravity and android.view.Gravity.TOP == android.view.Gravity.TOP) {
- topTo { parent.top() + layoutInfo.margin.top.toYInt() }
- } else if (layoutInfo.gravity and android.view.Gravity.BOTTOM == android.view.Gravity.BOTTOM) {
- bottomTo { parent.bottom() - layoutInfo.margin.bottom.toYInt() }
- } else {
- centerVerticallyTo { parent.centerY() }
- },
+ x =
+ if (layoutInfo.gravity and android.view.Gravity.CENTER_HORIZONTAL == android.view.Gravity.CENTER_HORIZONTAL) {
+ centerHorizontallyTo { parent.centerX() }
+ } else if (layoutInfo.gravity and android.view.Gravity.LEFT == android.view.Gravity.LEFT) {
+ leftTo { parent.left() + layoutInfo.margin.left.toXInt() }
+ } else if (layoutInfo.gravity and android.view.Gravity.RIGHT == android.view.Gravity.RIGHT) {
+ rightTo { parent.right() - layoutInfo.margin.right.toXInt() }
+ } else {
+ centerHorizontallyTo { parent.centerX() }
+ },
+ y =
+ if (layoutInfo.gravity and android.view.Gravity.CENTER_VERTICAL == android.view.Gravity.CENTER_VERTICAL) {
+ centerVerticallyTo { parent.centerY() }
+ } else if (layoutInfo.gravity and android.view.Gravity.TOP == android.view.Gravity.TOP) {
+ topTo { parent.top() + layoutInfo.margin.top.toYInt() }
+ } else if (layoutInfo.gravity and android.view.Gravity.BOTTOM == android.view.Gravity.BOTTOM) {
+ bottomTo { parent.bottom() - layoutInfo.margin.bottom.toYInt() }
+ } else {
+ centerVerticallyTo { parent.centerY() }
+ },
addToViewGroup = true,
)
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/abstract/AbstractMotionTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/abstract/AbstractMotionTextView.kt
index 9a158ffb..27ec8265 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/abstract/AbstractMotionTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/abstract/AbstractMotionTextView.kt
@@ -42,7 +42,6 @@ abstract class AbstractMotionTextView(
val highlightColor: String? = null,
effects: List = emptyList(),
) : BaseContourMotionView(context, startFrame, endFrame, effects = effects) {
-
/**
* Calculates the adjusted end frame based on the writing speed.
* If [writingSpeed] is 1.0, it matches [endFrame]. If higher, the duration is shortened.
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BlurEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BlurEffect.kt
index 3b6670a3..02fc2206 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BlurEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BlurEffect.kt
@@ -20,9 +20,9 @@ class BlurEffect(
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
-
+
val view = motionView as View
-
+
if (frame !in startFrame..endFrame) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
view.setRenderEffect(null)
@@ -43,8 +43,8 @@ class BlurEffect(
RenderEffect.createBlurEffect(
blurRadius,
blurRadius,
- Shader.TileMode.CLAMP
- )
+ Shader.TileMode.CLAMP,
+ ),
)
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BrightnessContrastEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BrightnessContrastEffect.kt
index 60491a67..912dbdde 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BrightnessContrastEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/BrightnessContrastEffect.kt
@@ -38,31 +38,50 @@ class BrightnessContrastEffect(
return motionView
}
- val brightness = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromBrightness, toBrightness),
- )
-
- val contrast = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromContrast, toContrast),
- )
+ val brightness =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromBrightness, toBrightness),
+ )
+
+ val contrast =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromContrast, toContrast),
+ )
// Matrix for contrast and brightness
// contrast * (channel - 0.5) + 0.5 + brightness
// = contrast * channel - 0.5 * contrast + 0.5 + brightness
val t = (1.0f - contrast) / 2.0f * 255.0f + brightness * 255.0f
-
- val matrix = floatArrayOf(
- contrast, 0f, 0f, 0f, t,
- 0f, contrast, 0f, 0f, t,
- 0f, 0f, contrast, 0f, t,
- 0f, 0f, 0f, 1f, 0f
- )
+
+ val matrix =
+ floatArrayOf(
+ contrast,
+ 0f,
+ 0f,
+ 0f,
+ t,
+ 0f,
+ contrast,
+ 0f,
+ 0f,
+ t,
+ 0f,
+ 0f,
+ contrast,
+ 0f,
+ t,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ )
val colorFilter = ColorMatrixColorFilter(matrix)
view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/ChainEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/ChainEffect.kt
index dde977a6..88761ef1 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/ChainEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/ChainEffect.kt
@@ -40,13 +40,13 @@ class ChainEffect(
}
// Chaining is tricky with the current side-effect based architecture.
- // For now, we just call the inner and outer effects, which will
+ // For now, we just call the inner and outer effects, which will
// each try to set the RenderEffect on the view, with the last one winning.
- // To properly support chaining, we would need to refactor MotionEffect
+ // To properly support chaining, we would need to refactor MotionEffect
// to return a RenderEffect instead of applying it.
innerEffect.forFrame(frame)
outerEffect.forFrame(frame)
-
+
return motionView
}
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeInEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeInEffect.kt
index 27f1fd69..8f10ac0e 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeInEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeInEffect.kt
@@ -15,9 +15,9 @@ class FadeInEffect(
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
-
+
val view = motionView as View
-
+
if (frame !in startFrame..endFrame) {
// If we are past the effect, ensure alpha is 1
if (frame > endFrame) {
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeOutEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeOutEffect.kt
index 1d153fe1..942e4f25 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeOutEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/FadeOutEffect.kt
@@ -15,9 +15,9 @@ class FadeOutEffect(
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
-
+
val view = motionView as View
-
+
if (frame !in startFrame..endFrame) {
// If we are past the effect, ensure alpha is 0
if (frame > endFrame) {
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/GrayscaleEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/GrayscaleEffect.kt
index e796d305..255440e3 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/GrayscaleEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/GrayscaleEffect.kt
@@ -32,7 +32,7 @@ class GrayscaleEffect(
if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
- // Keep the final state if needed, or clear it.
+ // Keep the final state if needed, or clear it.
// Typically transitions might want to stay at final state if it's the end of visibility.
// But for generic effects, we might want to clear them when out of range.
// BlurEffect clears it, so we follow that pattern.
@@ -41,17 +41,19 @@ class GrayscaleEffect(
return motionView
}
- val saturation = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromSaturation, toSaturation),
- )
+ val saturation =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromSaturation, toSaturation),
+ )
+
+ val matrix =
+ ColorMatrix().apply {
+ setSaturation(saturation)
+ }
- val matrix = ColorMatrix().apply {
- setSaturation(saturation)
- }
-
val colorFilter = ColorMatrixColorFilter(matrix)
view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/InvertEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/InvertEffect.kt
index c8c21157..763c73a8 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/InvertEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/InvertEffect.kt
@@ -35,12 +35,13 @@ class InvertEffect(
return motionView
}
- val intensity = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromIntensity, toIntensity),
- )
+ val intensity =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromIntensity, toIntensity),
+ )
// Invert matrix:
// R' = -1*R + 255
@@ -48,21 +49,55 @@ class InvertEffect(
// B' = -1*B + 255
// Scaled to 0-1 for ColorMatrix:
// R' = -1*R + 1
-
- val invertMatrix = floatArrayOf(
- -1f, 0f, 0f, 0f, 255f,
- 0f, -1f, 0f, 0f, 255f,
- 0f, 0f, -1f, 0f, 255f,
- 0f, 0f, 0f, 1f, 0f
- )
-
- val identityMatrix = floatArrayOf(
- 1f, 0f, 0f, 0f, 0f,
- 0f, 1f, 0f, 0f, 0f,
- 0f, 0f, 1f, 0f, 0f,
- 0f, 0f, 0f, 1f, 0f
- )
-
+
+ val invertMatrix =
+ floatArrayOf(
+ -1f,
+ 0f,
+ 0f,
+ 0f,
+ 255f,
+ 0f,
+ -1f,
+ 0f,
+ 0f,
+ 255f,
+ 0f,
+ 0f,
+ -1f,
+ 0f,
+ 255f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ )
+
+ val identityMatrix =
+ floatArrayOf(
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ )
+
val resultMatrix = FloatArray(20)
for (i in 0 until 20) {
resultMatrix[i] = identityMatrix[i] + (invertMatrix[i] - identityMatrix[i]) * intensity
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/OffsetEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/OffsetEffect.kt
index e8906213..05e719a1 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/OffsetEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/OffsetEffect.kt
@@ -36,19 +36,21 @@ class OffsetEffect(
return motionView
}
- val offsetX = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromOffsetX, toOffsetX),
- )
-
- val offsetY = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromOffsetY, toOffsetY),
- )
+ val offsetX =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromOffsetX, toOffsetX),
+ )
+
+ val offsetY =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromOffsetY, toOffsetY),
+ )
view.setRenderEffect(RenderEffect.createOffsetEffect(offsetX, offsetY))
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/PixelateEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/PixelateEffect.kt
index da4c3914..11ccbf56 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/PixelateEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/PixelateEffect.kt
@@ -22,7 +22,9 @@ class PixelateEffect(
) : MotionEffect {
override lateinit var motionView: MotionView
- private val PIXELATE_SHADER = """
+ companion object {
+ private const val PIXELATE_SHADER =
+ """
uniform shader content;
uniform float pixelSize;
@@ -33,7 +35,8 @@ class PixelateEffect(
float2 p = floor(fragCoord / pixelSize) * pixelSize;
return content.eval(p);
}
- """.trimIndent()
+ """
+ }
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
@@ -48,16 +51,17 @@ class PixelateEffect(
return motionView
}
- val pixelSize = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromPixelSize, toPixelSize),
- )
+ val pixelSize =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromPixelSize, toPixelSize),
+ )
val shader = RuntimeShader(PIXELATE_SHADER)
shader.setFloatUniform("pixelSize", pixelSize)
-
+
view.setRenderEffect(RenderEffect.createRuntimeShaderEffect(shader, "content"))
return motionView
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SepiaEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SepiaEffect.kt
index 510af887..3eb14dd5 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SepiaEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SepiaEffect.kt
@@ -1,6 +1,5 @@
package com.tejpratapsingh.motionlib.ui.effects
-import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.RenderEffect
import android.os.Build
@@ -36,33 +35,68 @@ class SepiaEffect(
return motionView
}
- val intensity = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(fromIntensity, toIntensity),
- )
+ val intensity =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromIntensity, toIntensity),
+ )
// Sepia matrix (standard)
// R' = (R * .393) + (G * .769) + (B * .189)
// G' = (R * .349) + (G * .686) + (B * .168)
// B' = (R * .272) + (G * .534) + (B * .131)
-
- val sepiaMatrix = floatArrayOf(
- 0.393f, 0.769f, 0.189f, 0f, 0f,
- 0.349f, 0.686f, 0.168f, 0f, 0f,
- 0.272f, 0.534f, 0.131f, 0f, 0f,
- 0f, 0f, 0f, 1f, 0f
- )
-
+
+ val sepiaMatrix =
+ floatArrayOf(
+ 0.393f,
+ 0.769f,
+ 0.189f,
+ 0f,
+ 0f,
+ 0.349f,
+ 0.686f,
+ 0.168f,
+ 0f,
+ 0f,
+ 0.272f,
+ 0.534f,
+ 0.131f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ )
+
// We can interpolate between identity and sepia
- val identityMatrix = floatArrayOf(
- 1f, 0f, 0f, 0f, 0f,
- 0f, 1f, 0f, 0f, 0f,
- 0f, 0f, 1f, 0f, 0f,
- 0f, 0f, 0f, 1f, 0f
- )
-
+ val identityMatrix =
+ floatArrayOf(
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ )
+
val resultMatrix = FloatArray(20)
for (i in 0 until 20) {
resultMatrix[i] = identityMatrix[i] + (sepiaMatrix[i] - identityMatrix[i]) * intensity
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VibrateEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VibrateEffect.kt
index 68c48724..2bfef10c 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VibrateEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VibrateEffect.kt
@@ -21,7 +21,7 @@ class VibrateEffect(
// Using sin wave for vibration
val offset = sin(frame.toDouble() * frequency).toFloat() * amplitude
-
+
view.translationX = offset
view.translationY = offset / 2f // Slight diagonal vibration
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VintageEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VintageEffect.kt
index 3d6223ec..af0a1f0f 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VintageEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/VintageEffect.kt
@@ -64,19 +64,51 @@ class VintageEffect(
// Sepia matrix (standard)
val sepiaMatrix =
floatArrayOf(
- 0.393f, 0.769f, 0.189f, 0f, 0f,
- 0.349f, 0.686f, 0.168f, 0f, 0f,
- 0.272f, 0.534f, 0.131f, 0f, 0f,
- 0f, 0f, 0f, 1f, 0f,
+ 0.393f,
+ 0.769f,
+ 0.189f,
+ 0f,
+ 0f,
+ 0.349f,
+ 0.686f,
+ 0.168f,
+ 0f,
+ 0f,
+ 0.272f,
+ 0.534f,
+ 0.131f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
)
// We can interpolate between identity and sepia
val identityMatrix =
floatArrayOf(
- 1f, 0f, 0f, 0f, 0f,
- 0f, 1f, 0f, 0f, 0f,
- 0f, 0f, 1f, 0f, 0f,
- 0f, 0f, 0f, 1f, 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ 1f,
+ 0f,
)
val resultMatrix = FloatArray(20)
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/ImageUtil.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/ImageUtil.kt
index 2bc18bf5..db944832 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/ImageUtil.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/ImageUtil.kt
@@ -14,39 +14,48 @@ import java.io.InputStream
object ImageUtil {
private val client = OkHttpClient()
- suspend fun fetchBitmap(context: Context, uri: Uri): Bitmap? = withContext(Dispatchers.IO) {
- val bitmap = when (uri.scheme) {
- "http", "https" -> fetchFromNetwork(uri.toString())
- "content", "file", "android.resource" -> fetchFromLocal(context, uri)
- else -> null
+ suspend fun fetchBitmap(
+ context: Context,
+ uri: Uri,
+ ): Bitmap? =
+ withContext(Dispatchers.IO) {
+ val bitmap =
+ when (uri.scheme) {
+ "http", "https" -> fetchFromNetwork(uri.toString())
+ "content", "file", "android.resource" -> fetchFromLocal(context, uri)
+ else -> null
+ }
+ return@withContext bitmap ?: fetchDefault(context)
}
- return@withContext bitmap ?: fetchDefault(context)
- }
- private fun fetchDefault(context: Context): Bitmap? {
- return try {
+ private fun fetchDefault(context: Context): Bitmap? =
+ try {
BitmapFactory.decodeResource(context.resources, R.drawable.default_bg)
} catch (e: Exception) {
null
}
- }
- private fun fetchFromNetwork(url: String): Bitmap? {
- return try {
- if (url.isBlank()) return null
- val request = Request.Builder().url(url).build()
- client.newCall(request).execute().use { response ->
- if (!response.isSuccessful) return null
- val bytes = response.body()?.bytes() ?: return null
- BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
+ private fun fetchFromNetwork(url: String): Bitmap? =
+ try {
+ if (url.isBlank()) {
+ null
+ } else {
+ val request = Request.Builder().url(url).build()
+ client.newCall(request).execute().use { response ->
+ if (!response.isSuccessful) return null
+ val bytes = response.body()?.bytes() ?: return null
+ BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
+ }
}
} catch (e: Exception) {
null
}
- }
- private fun fetchFromLocal(context: Context, uri: Uri): Bitmap? {
- return try {
+ private fun fetchFromLocal(
+ context: Context,
+ uri: Uri,
+ ): Bitmap? =
+ try {
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
inputStream.use {
BitmapFactory.decodeStream(it)
@@ -54,5 +63,4 @@ object ImageUtil {
} catch (e: Exception) {
null
}
- }
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/worker/MotionWorker.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/worker/MotionWorker.kt
index 76b5cb98..74a1796f 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/worker/MotionWorker.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/worker/MotionWorker.kt
@@ -45,7 +45,7 @@ abstract class MotionWorker(
override suspend fun doWork(): Result {
Timber.d("Worker ${this.id}: Starting video generation.")
- wakeLock.acquire(1 * 60 * 60 * 1000L /* 1 hours */)
+ wakeLock.acquire(1 * 60 * 60 * 1000L) // 1 hour
return try {
val videoFile: File =
generateVideo(
diff --git a/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/core/motion/MotionTransitionTest.kt b/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/core/motion/MotionTransitionTest.kt
index dee8947c..2500a1cc 100644
--- a/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/core/motion/MotionTransitionTest.kt
+++ b/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/core/motion/MotionTransitionTest.kt
@@ -12,10 +12,9 @@ import org.junit.Assert.assertTrue
import org.junit.Test
class MotionTransitionTest {
-
class MockMotionView(
override val startFrame: Int,
- override val endFrame: Int
+ override val endFrame: Int,
) : MotionView {
override var loop: Pair = Pair(0, 0)
override val effects: MutableList = mutableListOf()
@@ -25,9 +24,7 @@ class MotionTransitionTest {
effects.add(effect)
}
- override fun getViewBitmap(): Bitmap {
- throw UnsupportedOperationException()
- }
+ override fun getViewBitmap(): Bitmap = throw UnsupportedOperationException()
override fun forFrame(frame: Int): MotionView = this
}
@@ -36,29 +33,29 @@ class MotionTransitionTest {
fun testCrossFadeTransitionOverlap() {
val view1 = MockMotionView(0, 100)
val view2 = MockMotionView(101, 200)
-
+
val transition = CrossFadeTransition()
val duration = 20
-
+
transition.apply(view1, view2, duration)
-
+
// startFrame and endFrame should NOT be adjusted
assertEquals(0, view1.startFrame)
assertEquals(100, view1.endFrame)
assertEquals(101, view2.startFrame)
assertEquals(200, view2.endFrame)
-
+
// Effects should be added
assertTrue(view1.effects.any { it is FadeOutEffect })
assertTrue(view2.effects.any { it is FadeInEffect })
-
+
val fadeOut = view1.effects.first { it is FadeOutEffect }
val fadeIn = view2.effects.first { it is FadeInEffect }
-
+
// Transition centered at boundary 101: [101-10, 101+10-1] = [91, 110]
assertEquals(91, fadeOut.startFrame)
assertEquals(110, fadeOut.endFrame)
-
+
assertEquals(91, fadeIn.startFrame)
assertEquals(110, fadeIn.endFrame)
}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSDUIParsers.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSDUIParsers.kt
index 5ada249e..7ac1bd39 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSDUIParsers.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSDUIParsers.kt
@@ -89,10 +89,9 @@ fun JsonObject.getMotionPlugins(context: Context): List {
/**
* Get [MotionConfig] from [JsonObject].
*/
-fun JsonObject.getMotionConfig(): MotionConfig? {
- return if (has("config") && get("config").isJsonObject) {
+fun JsonObject.getMotionConfig(): MotionConfig? =
+ if (has("config") && get("config").isJsonObject) {
get("config").asJsonObject.toMotionConfig()
} else {
null
}
-}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSdui.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSdui.kt
index 90204b4d..24e4efa6 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSdui.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionSdui.kt
@@ -19,7 +19,8 @@ object MotionSdui {
private val effectSerializers = mutableMapOf, MotionEffectSerializer>()
private val transitionFactories = mutableMapOf()
- private val transitionSerializers = mutableMapOf, MotionTransitionSerializer>()
+ private val transitionSerializers =
+ mutableMapOf, MotionTransitionSerializer>()
private val pluginFactories = mutableMapOf()
private val pluginSerializers = mutableMapOf, MotionPluginSerializer>()
@@ -30,70 +31,100 @@ object MotionSdui {
/**
* Register a [MotionView] for deserialization.
*/
- fun registerView(type: String, factory: MotionViewFactory) {
+ fun registerView(
+ type: String,
+ factory: MotionViewFactory,
+ ) {
viewFactories[type] = factory
}
/**
* Register a [MotionView] for serialization.
*/
- fun registerViewSerializer(clazz: Class, serializer: MotionViewSerializer) {
+ fun registerViewSerializer(
+ clazz: Class,
+ serializer: MotionViewSerializer,
+ ) {
viewSerializers[clazz] = serializer
}
/**
* Register a [MotionEffect] for deserialization.
*/
- fun registerEffect(type: String, factory: MotionEffectFactory) {
+ fun registerEffect(
+ type: String,
+ factory: MotionEffectFactory,
+ ) {
effectFactories[type] = factory
}
/**
* Register a [MotionEffect] for serialization.
*/
- fun registerEffectSerializer(clazz: Class, serializer: MotionEffectSerializer) {
+ fun registerEffectSerializer(
+ clazz: Class,
+ serializer: MotionEffectSerializer,
+ ) {
effectSerializers[clazz] = serializer
}
/**
* Register a [MotionTransition] for deserialization.
*/
- fun registerTransition(type: String, factory: MotionTransitionFactory) {
+ fun registerTransition(
+ type: String,
+ factory: MotionTransitionFactory,
+ ) {
transitionFactories[type] = factory
}
/**
* Register a [MotionTransition] for serialization.
*/
- fun registerTransitionSerializer(clazz: Class, serializer: MotionTransitionSerializer) {
+ fun registerTransitionSerializer(
+ clazz: Class,
+ serializer: MotionTransitionSerializer,
+ ) {
transitionSerializers[clazz] = serializer
}
/**
* Register a [MotionPlugin] for deserialization.
*/
- fun registerPlugin(type: String, factory: MotionPluginFactory) {
+ fun registerPlugin(
+ type: String,
+ factory: MotionPluginFactory,
+ ) {
pluginFactories[type] = factory
}
/**
* Register a [MotionPlugin] for serialization.
*/
- fun registerPluginSerializer(clazz: Class, serializer: MotionPluginSerializer) {
+ fun registerPluginSerializer(
+ clazz: Class,
+ serializer: MotionPluginSerializer,
+ ) {
pluginSerializers[clazz] = serializer
}
/**
* Register a [MotionAudio] for deserialization.
*/
- fun registerAudio(type: String, factory: MotionAudioFactory) {
+ fun registerAudio(
+ type: String,
+ factory: MotionAudioFactory,
+ ) {
audioFactories[type] = factory
}
/**
* Register a [MotionAudio] for serialization.
*/
- fun registerAudioSerializer(clazz: Class, serializer: MotionAudioSerializer) {
+ fun registerAudioSerializer(
+ clazz: Class,
+ serializer: MotionAudioSerializer,
+ ) {
audioSerializers[clazz] = serializer
}
@@ -129,11 +160,17 @@ object MotionSdui {
}
fun interface MotionViewFactory {
- fun create(context: Context, json: JsonObject): MotionView
+ fun create(
+ context: Context,
+ json: JsonObject,
+ ): MotionView
}
fun interface MotionViewSerializer {
- fun serialize(view: T, json: JsonObject)
+ fun serialize(
+ view: T,
+ json: JsonObject,
+ )
}
fun interface MotionEffectFactory {
@@ -141,7 +178,10 @@ fun interface MotionEffectFactory {
}
fun interface MotionEffectSerializer {
- fun serialize(effect: T, json: JsonObject)
+ fun serialize(
+ effect: T,
+ json: JsonObject,
+ )
}
fun interface MotionTransitionFactory {
@@ -149,21 +189,36 @@ fun interface MotionTransitionFactory {
}
fun interface MotionTransitionSerializer {
- fun serialize(transition: T, json: JsonObject)
+ fun serialize(
+ transition: T,
+ json: JsonObject,
+ )
}
fun interface MotionPluginFactory {
- fun create(context: Context, json: JsonObject): MotionPlugin
+ fun create(
+ context: Context,
+ json: JsonObject,
+ ): MotionPlugin
}
fun interface MotionPluginSerializer {
- fun serialize(plugin: T, json: JsonObject)
+ fun serialize(
+ plugin: T,
+ json: JsonObject,
+ )
}
fun interface MotionAudioFactory {
- fun create(context: Context, json: JsonObject): MotionAudio
+ fun create(
+ context: Context,
+ json: JsonObject,
+ ): MotionAudio
}
fun interface MotionAudioSerializer {
- fun serialize(audio: T, json: JsonObject)
+ fun serialize(
+ audio: T,
+ json: JsonObject,
+ )
}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionTransitionParser.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionTransitionParser.kt
index bbb69afd..70534609 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionTransitionParser.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/MotionTransitionParser.kt
@@ -21,8 +21,12 @@ fun MotionTransition.toJson(): JsonObject {
* Polymorphic deserialization for [MotionTransition].
*/
fun JsonObject.toMotionTransition(): MotionTransition {
- val type = get("type")?.asString ?: throw IllegalArgumentException("Missing 'type' in MotionTransition JSON")
- val factory = MotionSdui.getTransitionFactory(type) ?: throw IllegalArgumentException("No factory registered for MotionTransition type: $type")
+ val type =
+ get("type")?.asString
+ ?: throw IllegalArgumentException("Missing 'type' in MotionTransition JSON")
+ val factory =
+ MotionSdui.getTransitionFactory(type)
+ ?: throw IllegalArgumentException("No factory registered for MotionTransition type: $type")
return factory.create(this)
}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/VideoAspectRatioParser.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/VideoAspectRatioParser.kt
index db0ff06b..2f07e228 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/VideoAspectRatioParser.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/infra/VideoAspectRatioParser.kt
@@ -62,6 +62,4 @@ private val gsonDefault = Gson()
fun VideoAspectRatio.toJson(): JsonObject = gsonDefault.toJsonTree(this).asJsonObject
-fun JsonObject.toVideoAspectRatio(): VideoAspectRatio {
- return gsonWithAspectRatio.fromJson(this, VideoAspectRatio::class.java)
-}
+fun JsonObject.toVideoAspectRatio(): VideoAspectRatio = gsonWithAspectRatio.fromJson(this, VideoAspectRatio::class.java)
diff --git a/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/MotionTemplateInstrumentationTest.kt b/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/MotionTemplateInstrumentationTest.kt
index 437bc69d..b62ec16f 100644
--- a/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/MotionTemplateInstrumentationTest.kt
+++ b/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/MotionTemplateInstrumentationTest.kt
@@ -18,79 +18,87 @@ import java.io.File
@RunWith(AndroidJUnit4::class)
class MotionTemplateInstrumentationTest {
-
- data class LyricLine(val text: String, val startFrame: Int, val endFrame: Int)
+ data class LyricLine(
+ val text: String,
+ val startFrame: Int,
+ val endFrame: Int,
+ )
@Test
fun testHardExampleWithRealContext() {
val appContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
-
+
// We need a real MotionVideoProducer for the content block to execute addView/audio calls
val producer = MotionVideoProducer.with(appContext)
- val lyricsVideoTemplate = motionTemplate("Lyrics Master") {
- parameters {
- string("songTitle")
- string("backgroundVideoPath")
- }
+ val lyricsVideoTemplate =
+ motionTemplate("Lyrics Master") {
+ parameters {
+ string("songTitle")
+ string("backgroundVideoPath")
+ }
- content {
- val bgVideoPath = data.getString("backgroundVideoPath")
- assertNotNull(bgVideoPath)
-
- // Add a dummy background video view using the DSL extension
- // Even if the path is fake, we're testing the DSL registration
- videoFrameView(
- videoUri = android.net.Uri.parse(bgVideoPath!!),
- startFrame = 0,
- endFrame = 1000
- )
-
- // Fetch list of lyrics from data
- val lyrics = data.get>("lyricLines") ?: emptyList()
- assertEquals(2, lyrics.size)
-
- lyrics.forEach { line ->
- // Use the DSL to add text views for lyrics
- popUpTextView(
- text = line.text,
- startFrame = line.startFrame,
- endFrame = line.endFrame
+ content {
+ val bgVideoPath = data.getString("backgroundVideoPath")
+ assertNotNull(bgVideoPath)
+
+ // Add a dummy background video view using the DSL extension
+ // Even if the path is fake, we're testing the DSL registration
+ videoFrameView(
+ videoUri = android.net.Uri.parse(bgVideoPath!!),
+ startFrame = 0,
+ endFrame = 1000,
)
- }
- // Overlay Song Title
- val songTitle = data.getString("songTitle")
- assertEquals("Amazing Grace", songTitle)
-
- typeWriterTextView(
- text = songTitle!!,
- startFrame = 0,
- endFrame = 60
- )
-
- // Test audio function
- audio(File(appContext.cacheDir, "test_audio.mp3"), startFrame = 0, endFrame = 500)
+ // Fetch list of lyrics from data
+ val lyrics = data.get>("lyricLines") ?: emptyList()
+ assertEquals(2, lyrics.size)
+
+ lyrics.forEach { line ->
+ // Use the DSL to add text views for lyrics
+ popUpTextView(
+ text = line.text,
+ startFrame = line.startFrame,
+ endFrame = line.endFrame,
+ )
+ }
+
+ // Overlay Song Title
+ val songTitle = data.getString("songTitle")
+ assertEquals("Amazing Grace", songTitle)
+
+ typeWriterTextView(
+ text = songTitle!!,
+ startFrame = 0,
+ endFrame = 60,
+ )
+
+ // Test audio function
+ audio(File(appContext.cacheDir, "test_audio.mp3"), startFrame = 0, endFrame = 500)
+ }
}
- }
assertEquals("Lyrics Master", lyricsVideoTemplate.name)
// Mock data
- val lyricsData = listOf(
- LyricLine("Amazing grace how sweet the sound", 0, 100),
- LyricLine("That saved a wretch like me", 101, 200)
- )
-
- val data = TemplateData(mapOf(
- "songTitle" to "Amazing Grace",
- "backgroundVideoPath" to "/path/to/video.mp4",
- "lyricLines" to lyricsData
- ))
+ val lyricsData =
+ listOf(
+ LyricLine("Amazing grace how sweet the sound", 0, 100),
+ LyricLine("That saved a wretch like me", 101, 200),
+ )
+
+ val data =
+ TemplateData(
+ mapOf(
+ "songTitle" to "Amazing Grace",
+ "backgroundVideoPath" to "/path/to/video.mp4",
+ "lyricLines" to lyricsData,
+ ),
+ )
// Create the scope with REAL context and producer
val scope = ContentScope(appContext, producer, data)
-
+
// This will now execute the content block, including DSL extension calls
lyricsVideoTemplate.buildContent(scope)
diff --git a/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/sdui/MotionTemplateSDUIProviderTest.kt b/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/sdui/MotionTemplateSDUIProviderTest.kt
index 88a2e1e0..e9955676 100644
--- a/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/sdui/MotionTemplateSDUIProviderTest.kt
+++ b/modules/templates/src/androidTest/java/com/tejpratapsingh/motionlib/templates/sdui/MotionTemplateSDUIProviderTest.kt
@@ -15,35 +15,39 @@ import java.io.File
@RunWith(AndroidJUnit4::class)
class MotionTemplateSDUIProviderTest {
-
@Test
fun testProvideSDUI() {
val appContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
- val template = motionTemplate("Test Template") {
- parameters {
- string("title")
- }
- content {
- val title = data.getString("title") ?: "Default"
- popUpTextView(
- text = title,
- startFrame = 0,
- endFrame = 100
- )
- audio(File(context.cacheDir, "test.mp3"), startFrame = 0, endFrame = 100)
+ val template =
+ motionTemplate("Test Template") {
+ parameters {
+ string("title")
+ }
+ content {
+ val title = data.getString("title") ?: "Default"
+ popUpTextView(
+ text = title,
+ startFrame = 0,
+ endFrame = 100,
+ )
+ audio(File(context.cacheDir, "test.mp3"), startFrame = 0, endFrame = 100)
+ }
}
- }
- val data = TemplateData(mapOf(
- "title" to "Hello SDUI"
- ))
+ val data =
+ TemplateData(
+ mapOf(
+ "title" to "Hello SDUI",
+ ),
+ )
- val sdui = MotionTemplateSDUIProvider.provideSDUI(
- context = appContext,
- template = template,
- data = data
- )
+ val sdui =
+ MotionTemplateSDUIProvider.provideSDUI(
+ context = appContext,
+ template = template,
+ data = data,
+ )
assertNotNull(sdui)
assertTrue(sdui.has("views"))
@@ -52,10 +56,10 @@ class MotionTemplateSDUIProviderTest {
val viewsArray = sdui.getAsJsonArray("views")
assertEquals(1, viewsArray.size())
-
+
val firstView = viewsArray[0].asJsonObject
assertTrue(firstView.has("type"))
-
+
val audiosArray = sdui.getAsJsonArray("audios")
assertEquals(1, audiosArray.size())
}
diff --git a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/json/JsonMotionTemplate.kt b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/json/JsonMotionTemplate.kt
index e7783e86..5fb76f95 100644
--- a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/json/JsonMotionTemplate.kt
+++ b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/json/JsonMotionTemplate.kt
@@ -11,13 +11,12 @@ import com.tejpratapsingh.motionlib.templates.serialization.TemplateSerializatio
class JsonMotionTemplate(
name: String,
parameters: List>,
- val rawContent: JsonObject
+ val rawContent: JsonObject,
) : MotionTemplate(name, parameters) {
-
override fun buildContent(scope: ContentScope) {
// 1. Apply data to content JSON
val appliedJson = TemplateSerialization.applyData(rawContent, scope.data).asJsonObject
-
+
// 2. Content can be a single view or a container with children
// For simplicity, let's assume it's a list of views under "views" key or a single view
if (appliedJson.has("views")) {
diff --git a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/MotionTemplate.kt b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/MotionTemplate.kt
index 84174ae4..109625af 100644
--- a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/MotionTemplate.kt
+++ b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/MotionTemplate.kt
@@ -4,7 +4,7 @@ import com.tejpratapsingh.motionlib.templates.dsl.ContentScope
abstract class MotionTemplate(
val name: String,
- val parameters: List>
+ val parameters: List>,
) {
abstract fun buildContent(scope: ContentScope)
diff --git a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateData.kt b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateData.kt
index 18903f50..6c9537dc 100644
--- a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateData.kt
+++ b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateData.kt
@@ -1,11 +1,16 @@
package com.tejpratapsingh.motionlib.templates.model
-class TemplateData(private val values: Map) {
+class TemplateData(
+ private val values: Map,
+) {
@Suppress("UNCHECKED_CAST")
fun get(name: String): T? = values[name] as? T
fun getString(name: String): String? = get(name)
+
fun getInt(name: String): Int? = get(name)
+
fun getFloat(name: String): Float? = get(name)
+
fun getBoolean(name: String): Boolean? = get(name)
}
diff --git a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateParameter.kt b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateParameter.kt
index bdb7185d..17ce8453 100644
--- a/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateParameter.kt
+++ b/modules/templates/src/main/java/com/tejpratapsingh/motionlib/templates/model/TemplateParameter.kt
@@ -1,12 +1,18 @@
package com.tejpratapsingh.motionlib.templates.model
enum class ParameterType {
- STRING, INTEGER, FLOAT, COLOR, BOOLEAN, IMAGE, VIDEO
+ STRING,
+ INTEGER,
+ FLOAT,
+ COLOR,
+ BOOLEAN,
+ IMAGE,
+ VIDEO,
}
data class TemplateParameter(
val name: String,
val type: ParameterType,
val defaultValue: T? = null,
- val description: String? = null
+ val description: String? = null,
)
diff --git a/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/TemplateSerializationTest.kt b/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/TemplateSerializationTest.kt
index 17419cab..9db99e18 100644
--- a/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/TemplateSerializationTest.kt
+++ b/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/TemplateSerializationTest.kt
@@ -12,10 +12,10 @@ import org.junit.Assert.assertTrue
import org.junit.Test
class TemplateSerializationTest {
-
@Test
fun testPlaceholderReplacement() {
- val json = """
+ val json =
+ """
{
"type": "PopUpTextView",
"text": "{{title}}",
@@ -25,17 +25,20 @@ class TemplateSerializationTest {
"key": "Value: {{title}}"
}
}
- """.trimIndent()
-
+ """.trimIndent()
+
val content = JsonParser.parseString(json).asJsonObject
- val data = TemplateData(mapOf(
- "title" to "Hello World",
- "duration" to 300,
- "color" to 0xFF00FF
- ))
-
+ val data =
+ TemplateData(
+ mapOf(
+ "title" to "Hello World",
+ "duration" to 300,
+ "color" to 0xFF00FF,
+ ),
+ )
+
val applied = TemplateSerialization.applyData(content, data).asJsonObject
-
+
assertEquals("Hello World", applied.get("text").asString)
assertEquals(300, applied.get("duration").asInt)
assertEquals(0xFF00FF, applied.get("color").asInt)
@@ -44,22 +47,24 @@ class TemplateSerializationTest {
@Test
fun testTemplateSerialization() {
- val parameters = listOf(
- TemplateParameter("title", ParameterType.STRING, "Default"),
- TemplateParameter("duration", ParameterType.INTEGER, 100)
- )
- val content = JsonObject().apply {
- addProperty("type", "SimpleView")
- addProperty("text", "{{title}}")
- }
-
+ val parameters =
+ listOf(
+ TemplateParameter("title", ParameterType.STRING, "Default"),
+ TemplateParameter("duration", ParameterType.INTEGER, 100),
+ )
+ val content =
+ JsonObject().apply {
+ addProperty("type", "SimpleView")
+ addProperty("text", "{{title}}")
+ }
+
val template = JsonMotionTemplate("MyTemplate", parameters, content)
val json = TemplateSerialization.templateToJson(template)
-
+
assertEquals("MyTemplate", json.get("name").asString)
assertEquals(2, json.getAsJsonArray("parameters").size())
assertTrue(json.has("content"))
-
+
val restoredTemplate = TemplateSerialization.templateFromJson(json)
assertEquals(template.name, restoredTemplate.name)
assertEquals(template.parameters.size, restoredTemplate.parameters.size)
@@ -68,7 +73,8 @@ class TemplateSerializationTest {
@Test
fun testArrayReplication() {
- val json = """
+ val json =
+ """
{
"views": [
{
@@ -84,25 +90,35 @@ class TemplateSerializationTest {
}
]
}
- """.trimIndent()
-
+ """.trimIndent()
+
val content = JsonParser.parseString(json).asJsonObject
- val data = TemplateData(mapOf(
- "items" to listOf(
- mapOf("text" to "Item 1", "frame" to 10),
- mapOf("text" to "Item 2", "frame" to 20)
+ val data =
+ TemplateData(
+ mapOf(
+ "items" to
+ listOf(
+ mapOf("text" to "Item 1", "frame" to 10),
+ mapOf("text" to "Item 2", "frame" to 20),
+ ),
+ ),
)
- ))
-
+
val applied = TemplateSerialization.applyData(content, data).asJsonObject
val views = applied.getAsJsonArray("views")
-
+
assertEquals(3, views.size()) // 1 static + 2 dynamic
- assertEquals("StaticView", views.get(0).asJsonObject.get("type").asString)
- assertEquals("DynamicView", views.get(1).asJsonObject.get("type").asString)
- assertEquals("Item 1", views.get(1).asJsonObject.get("text").asString)
- assertEquals(10, views.get(1).asJsonObject.get("frame").asInt)
- assertEquals("Item 2", views.get(2).asJsonObject.get("text").asString)
- assertEquals(20, views.get(2).asJsonObject.get("frame").asInt)
+
+ val view0 = views.get(0).asJsonObject
+ assertEquals("StaticView", view0.get("type").asString)
+
+ val view1 = views.get(1).asJsonObject
+ assertEquals("DynamicView", view1.get("type").asString)
+ assertEquals("Item 1", view1.get("text").asString)
+ assertEquals(10, view1.get("frame").asInt)
+
+ val view2 = views.get(2).asJsonObject
+ assertEquals("Item 2", view2.get("text").asString)
+ assertEquals(20, view2.get("frame").asInt)
}
}