diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7f..35eb1ddf 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 2fe43efa..d38202e3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,5 @@
+import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
@@ -5,7 +7,19 @@ buildscript {
mavenCentral()
}
dependencies {
+ classpath libs.ktlint.gradle
}
}
plugins {
+ alias(libs.plugins.ktlint)
+}
+ktlint {
+ android.set(true) // set true for Android projects
+ outputToConsole.set(true)
+ ignoreFailures.set(false)
+ enableExperimentalRules.set(false)
+ reporters {
+ reporter(ReporterType.PLAIN)
+ reporter(ReporterType.CHECKSTYLE)
+ }
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 277ba6d9..736ec4ed 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -18,7 +18,7 @@ work_version = "2.10.3"
junit_version = "4.13.2"
androidx_test_ext_junit_version = "1.3.0"
espresso_core_version = "3.7.0"
-agpVersion = "8.13.1"
+agpVersion = "8.13.2"
kotlinVersion = "2.2.10"
pytorchAndroidTorchCisionLite = "2.1.0"
kotlinGradlePluginVersion = "2.2.0"
@@ -27,6 +27,7 @@ gson = "2.13.1"
activityComposeVersion = "1.10.1"
composeBomVersion = "2024.09.00"
navigationComposeVersion = "2.9.3"
+ktlint = "14.0.1"
[libraries]
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayoutVersion" }
@@ -48,6 +49,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoupVersion" }
junit = { group = "junit", name = "junit", version.ref = "junit_version" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx_test_ext_junit_version" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso_core_version" }
+ktlint-gradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlint" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCoreVersion" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorClientCoreVersion" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCoreVersion" }
@@ -81,6 +83,7 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVer
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinVersion" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlinVersion" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinVersion" }
+ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
[bundles]
androidx = [
diff --git a/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/Filament3dView.kt b/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/Filament3dView.kt
index 04058846..374c35a7 100644
--- a/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/Filament3dView.kt
+++ b/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/Filament3dView.kt
@@ -31,9 +31,9 @@ class Filament3dView(
private val modelAssetPath: String,
override val startFrame: Int,
override val endFrame: Int,
- override val loop: Pair = Pair(0, 0)
-) : FrameLayout(context), MotionView {
-
+ override val loop: Pair = Pair(0, 0),
+) : FrameLayout(context),
+ MotionView {
override val effects: List = emptyList()
companion object {
@@ -50,9 +50,10 @@ class Filament3dView(
private lateinit var surfaceTexture: SurfaceTexture
private lateinit var surface: Surface
- private val imageView = ImageView(context).apply {
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
- }
+ private val imageView =
+ ImageView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
+ }
init {
initializeFilament()
@@ -67,7 +68,7 @@ class Filament3dView(
surfaceTexture = SurfaceTexture(0)
surfaceTexture.setDefaultBufferSize(
MotionConfig.aspectRatio.width,
- MotionConfig.aspectRatio.height
+ MotionConfig.aspectRatio.height,
)
surface = Surface(surfaceTexture)
engine = Engine.create()
@@ -109,12 +110,18 @@ class Filament3dView(
(MotionConfig.aspectRatio.width / MotionConfig.aspectRatio.height).toDouble(),
0.1,
1000.0,
- Camera.Fov.VERTICAL
+ Camera.Fov.VERTICAL,
)
camera.lookAt(
- 0.0, 0.0, 5.0, // eyeX, eyeY, eyeZ
- 0.0, 0.0, 0.0, // centerX, centerY, centerZ
- 0.0, 1.0, 0.0 // upX, upY, upZ
+ 0.0,
+ 0.0,
+ 5.0, // eyeX, eyeY, eyeZ
+ 0.0,
+ 0.0,
+ 0.0, // centerX, centerY, centerZ
+ 0.0,
+ 1.0,
+ 0.0, // upX, upY, upZ
)
view.camera = camera
}
@@ -136,9 +143,15 @@ class Filament3dView(
val height = view.viewport.height
val buffer = ByteBuffer.allocateDirect(width * height * 4)
renderer.readPixels(
- 0, 0, width, height, Texture.PixelBufferDescriptor(
- buffer, Texture.Format.RGBA, Texture.Type.UBYTE
- )
+ 0,
+ 0,
+ width,
+ height,
+ Texture.PixelBufferDescriptor(
+ buffer,
+ Texture.Format.RGBA,
+ Texture.Type.UBYTE,
+ ),
)
buffer.rewind()
renderer.endFrame()
@@ -190,4 +203,4 @@ class Filament3dView(
fun destroy() {
cleanupFilament()
}
-}
\ No newline at end of file
+}
diff --git a/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/FilamentOffscreenCapturer.kt b/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/FilamentOffscreenCapturer.kt
index 2fab58df..70f9c1c7 100644
--- a/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/FilamentOffscreenCapturer.kt
+++ b/modules/3d-filament-renderer/src/main/java/com/tejpratapsingh/motionlib/filamentrenderer/FilamentOffscreenCapturer.kt
@@ -24,7 +24,9 @@ import java.nio.ByteOrder
import kotlin.math.cos
import kotlin.math.sin
-class FilamentOffscreenCapturer(private val context: Context) {
+class FilamentOffscreenCapturer(
+ private val context: Context,
+) {
private lateinit var engine: Engine
private var renderer: Renderer? = null
private var scene: Scene? = null
@@ -39,10 +41,15 @@ class FilamentOffscreenCapturer(private val context: Context) {
private var asset: FilamentAsset? = null
enum class RotationAxis {
- X, Y, Z
+ X,
+ Y,
+ Z,
}
- fun init(width: Int, height: Int) {
+ fun init(
+ width: Int,
+ height: Int,
+ ) {
engine = Engine.create()
renderer = engine.createRenderer()
@@ -65,12 +72,18 @@ class FilamentOffscreenCapturer(private val context: Context) {
width.toDouble() / height.toDouble(),
0.1,
100.0,
- Camera.Fov.VERTICAL
+ Camera.Fov.VERTICAL,
)
camera?.lookAt(
- 0.0, 0.0, 3.0, // eye
- 0.0, 0.0, 0.0, // center
- 0.0, 1.0, 0.0 // up
+ 0.0,
+ 0.0,
+ 3.0, // eye
+ 0.0,
+ 0.0,
+ 0.0, // center
+ 0.0,
+ 1.0,
+ 0.0, // up
)
val materialProvider = UbershaderProvider(engine)
@@ -108,7 +121,10 @@ class FilamentOffscreenCapturer(private val context: Context) {
scene?.addEntities(asset!!.entities)
}
- fun setRotation(axis: RotationAxis, degrees: Float) {
+ fun setRotation(
+ axis: RotationAxis,
+ degrees: Float,
+ ) {
asset?.let {
val radians = Math.toRadians(degrees.toDouble())
val cos = cos(radians).toFloat()
@@ -118,26 +134,62 @@ class FilamentOffscreenCapturer(private val context: Context) {
when (axis) {
RotationAxis.X -> {
// Rotate around X-axis
- transform[0] = 1f; transform[4] = 0f; transform[8] = 0f; transform[12] = 0f
- transform[1] = 0f; transform[5] = cos; transform[9] = -sin; transform[13] = 0f
- transform[2] = 0f; transform[6] = sin; transform[10] = cos; transform[14] = 0f
- transform[3] = 0f; transform[7] = 0f; transform[11] = 0f; transform[15] = 1f
+ transform[0] = 1f
+ transform[4] = 0f
+ transform[8] = 0f
+ transform[12] = 0f
+ transform[1] = 0f
+ transform[5] = cos
+ transform[9] = -sin
+ transform[13] = 0f
+ transform[2] = 0f
+ transform[6] = sin
+ transform[10] = cos
+ transform[14] = 0f
+ transform[3] = 0f
+ transform[7] = 0f
+ transform[11] = 0f
+ transform[15] = 1f
}
RotationAxis.Y -> {
// Rotate around Y-axis
- transform[0] = cos; transform[4] = 0f; transform[8] = sin; transform[12] = 0f
- transform[1] = 0f; transform[5] = 1f; transform[9] = 0f; transform[13] = 0f
- transform[2] = -sin; transform[6] = 0f; transform[10] = cos; transform[14] = 0f
- transform[3] = 0f; transform[7] = 0f; transform[11] = 0f; transform[15] = 1f
+ transform[0] = cos
+ transform[4] = 0f
+ transform[8] = sin
+ transform[12] = 0f
+ transform[1] = 0f
+ transform[5] = 1f
+ transform[9] = 0f
+ transform[13] = 0f
+ transform[2] = -sin
+ transform[6] = 0f
+ transform[10] = cos
+ transform[14] = 0f
+ transform[3] = 0f
+ transform[7] = 0f
+ transform[11] = 0f
+ transform[15] = 1f
}
RotationAxis.Z -> {
// Rotate around Z-axis
- transform[0] = cos; transform[4] = -sin; transform[8] = 0f; transform[12] = 0f
- transform[1] = sin; transform[5] = cos; transform[9] = 0f; transform[13] = 0f
- transform[2] = 0f; transform[6] = 0f; transform[10] = 1f; transform[14] = 0f
- transform[3] = 0f; transform[7] = 0f; transform[11] = 0f; transform[15] = 1f
+ transform[0] = cos
+ transform[4] = -sin
+ transform[8] = 0f
+ transform[12] = 0f
+ transform[1] = sin
+ transform[5] = cos
+ transform[9] = 0f
+ transform[13] = 0f
+ transform[2] = 0f
+ transform[6] = 0f
+ transform[10] = 1f
+ transform[14] = 0f
+ transform[3] = 0f
+ transform[7] = 0f
+ transform[11] = 0f
+ transform[15] = 1f
}
}
@@ -161,7 +213,10 @@ class FilamentOffscreenCapturer(private val context: Context) {
setRotation(RotationAxis.Z, degrees)
}
- fun capture(width: Int, height: Int): Bitmap? {
+ fun capture(
+ width: Int,
+ height: Int,
+ ): Bitmap? {
try {
if (renderer?.beginFrame(swapChain!!, 0L) == true) {
renderer?.render(view!!)
@@ -170,11 +225,12 @@ class FilamentOffscreenCapturer(private val context: Context) {
val pixelCount = width * height
val buf = ByteBuffer.allocateDirect(pixelCount * 4).order(ByteOrder.nativeOrder())
- val descriptor = Texture.PixelBufferDescriptor(
- buf,
- Texture.Format.RGBA,
- Texture.Type.UBYTE
- )
+ val descriptor =
+ Texture.PixelBufferDescriptor(
+ buf,
+ Texture.Format.RGBA,
+ Texture.Type.UBYTE,
+ )
renderer?.readPixels(0, 0, width, height, descriptor)
buf.rewind()
diff --git a/modules/3d-filament-renderer/src/test/java/com/tejpratapsingh/motionlib/filamentrenderer/ExampleUnitTest.kt b/modules/3d-filament-renderer/src/test/java/com/tejpratapsingh/motionlib/filamentrenderer/ExampleUnitTest.kt
index 18e34b09..38697d2e 100644
--- a/modules/3d-filament-renderer/src/test/java/com/tejpratapsingh/motionlib/filamentrenderer/ExampleUnitTest.kt
+++ b/modules/3d-filament-renderer/src/test/java/com/tejpratapsingh/motionlib/filamentrenderer/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/MotionOpenGlView.kt b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/MotionOpenGlView.kt
index d5adc770..c0acdead 100644
--- a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/MotionOpenGlView.kt
+++ b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/MotionOpenGlView.kt
@@ -13,24 +13,26 @@ class MotionOpenGlView(
modelAssetPath: String,
override val startFrame: Int,
override val endFrame: Int,
- override val loop: Pair = Pair(0, 0)
-) : FrameLayout(context), MotionView {
-
+ override val loop: Pair = Pair(0, 0),
+) : FrameLayout(context),
+ MotionView {
override val effects: List = emptyList()
- private val imageView = ImageView(context).apply {
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
- }
+ private val imageView =
+ ImageView(context).apply {
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
+ }
// private val offscreenRenderer: OffscreenRenderer
- val offscreenRenderer = Object3DToBitmapRenderer(
- context = context,
- assetFileName = modelAssetPath,
- width = MotionConfig.aspectRatio.width,
- height = MotionConfig.aspectRatio.height,
- objectColor = floatArrayOf(0.7f, 0.3f, 0.3f, 1.0f)
- )
+ val offscreenRenderer =
+ Object3DToBitmapRenderer(
+ context = context,
+ assetFileName = modelAssetPath,
+ width = MotionConfig.aspectRatio.width,
+ height = MotionConfig.aspectRatio.height,
+ objectColor = floatArrayOf(0.7f, 0.3f, 0.3f, 1.0f),
+ )
init {
// Initialize OpenGL renderer with the model asset path
@@ -53,4 +55,4 @@ class MotionOpenGlView(
}
override fun getViewBitmap(): Bitmap = offscreenRenderer.generateBitmap()!!
-}
\ No newline at end of file
+}
diff --git a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/ObjModel.kt b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/ObjModel.kt
index 01c62ff0..6dbbe229 100644
--- a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/ObjModel.kt
+++ b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/ObjModel.kt
@@ -8,7 +8,10 @@ import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.nio.ShortBuffer
-class ObjModel(context: Context, filename: String) {
+class ObjModel(
+ context: Context,
+ filename: String,
+) {
val vertexBuffer: FloatBuffer
val indexBuffer: ShortBuffer
val indexCount: Int
@@ -35,7 +38,7 @@ class ObjModel(context: Context, filename: String) {
for (i in 1..3) {
val indexStr = parts[i].split("/")[0]
val idx = indexStr.toInt()
- indices.add((idx - 1).toShort()) // OBJ is 1-based
+ indices.add((idx - 1).toShort()) // OBJ is 1-based
}
}
}
@@ -45,22 +48,26 @@ class ObjModel(context: Context, filename: String) {
vertices.addAll(it.toList())
}
- vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
- .order(ByteOrder.nativeOrder())
- .asFloatBuffer()
- .apply {
- put(vertices.toFloatArray())
- position(0)
- }
+ vertexBuffer =
+ ByteBuffer
+ .allocateDirect(vertices.size * 4)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer()
+ .apply {
+ put(vertices.toFloatArray())
+ position(0)
+ }
- indexBuffer = ByteBuffer.allocateDirect(indices.size * 2)
- .order(ByteOrder.nativeOrder())
- .asShortBuffer()
- .apply {
- put(indices.toShortArray())
- position(0)
- }
+ indexBuffer =
+ ByteBuffer
+ .allocateDirect(indices.size * 2)
+ .order(ByteOrder.nativeOrder())
+ .asShortBuffer()
+ .apply {
+ put(indices.toShortArray())
+ position(0)
+ }
indexCount = indices.size
}
-}
\ No newline at end of file
+}
diff --git a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/Object3DToBitmapRenderer.kt b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/Object3DToBitmapRenderer.kt
index e5905bd7..54df692d 100644
--- a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/Object3DToBitmapRenderer.kt
+++ b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/Object3DToBitmapRenderer.kt
@@ -23,9 +23,8 @@ class Object3DToBitmapRenderer(
private val assetFileName: String,
private val width: Int,
private val height: Int,
- private val objectColor: FloatArray = floatArrayOf(0.8f, 0.8f, 0.8f, 1.0f)
+ private val objectColor: FloatArray = floatArrayOf(0.8f, 0.8f, 0.8f, 1.0f),
) {
-
private var egl: EGL10? = null
private var eglDisplay: EGLDisplay? = null
private var eglContext: EGLContext? = null
@@ -50,10 +49,11 @@ class Object3DToBitmapRenderer(
val vertices: FloatBuffer,
val normals: FloatBuffer,
val indices: ShortBuffer,
- val indexCount: Int
+ val indexCount: Int,
)
- private val vertexShaderCode = """
+ private val vertexShaderCode =
+ """
attribute vec4 vPosition;
attribute vec3 vNormal;
uniform mat4 uMVPMatrix;
@@ -72,9 +72,10 @@ class Object3DToBitmapRenderer(
diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));
vLightIntensity = diffuse;
}
- """.trimIndent()
+ """.trimIndent()
- private val fragmentShaderCode = """
+ private val fragmentShaderCode =
+ """
precision mediump float;
uniform vec4 uColor;
varying float vLightIntensity;
@@ -82,14 +83,14 @@ class Object3DToBitmapRenderer(
void main() {
gl_FragColor = vec4(uColor.rgb * vLightIntensity, uColor.a);
}
- """.trimIndent()
+ """.trimIndent()
/**
* Initialize the renderer - call this once before generating bitmaps
* @return true if initialization successful, false otherwise
*/
- fun initialize(): Boolean {
- return try {
+ fun initialize(): Boolean =
+ try {
setupOffscreenRendering(width, height)
mesh = loadObjFromAssets(assetFileName)
setupShaders()
@@ -101,7 +102,6 @@ class Object3DToBitmapRenderer(
cleanup()
false
}
- }
/**
* Set the rotation angles for the 3D model
@@ -112,7 +112,7 @@ class Object3DToBitmapRenderer(
fun setRotation(
rotationX: Float = 0f,
rotationY: Float = 0f,
- rotationZ: Float = 0f
+ rotationZ: Float = 0f,
) {
if (!isInitialized) {
throw IllegalStateException("Renderer not initialized. Call initialize() first.")
@@ -152,7 +152,7 @@ class Object3DToBitmapRenderer(
fun generateBitmapWithRotation(
rotationX: Float = 0f,
rotationY: Float = 0f,
- rotationZ: Float = 0f
+ rotationZ: Float = 0f,
): Bitmap? {
setRotation(rotationX, rotationY, rotationZ)
return generateBitmap()
@@ -167,44 +167,60 @@ class Object3DToBitmapRenderer(
mesh = null
}
- private fun setupOffscreenRendering(width: Int, height: Int) {
+ private fun setupOffscreenRendering(
+ width: Int,
+ height: Int,
+ ) {
egl = EGL10.EGL_NO_CONTEXT as EGL10
eglDisplay = egl!!.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
val version = IntArray(2)
egl!!.eglInitialize(eglDisplay, version)
- val configAttribs = intArrayOf(
- EGL10.EGL_RENDERABLE_TYPE, 4, // EGL_OPENGL_ES2_BIT
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 8,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- )
+ val configAttribs =
+ intArrayOf(
+ EGL10.EGL_RENDERABLE_TYPE,
+ 4, // EGL_OPENGL_ES2_BIT
+ EGL10.EGL_RED_SIZE,
+ 8,
+ EGL10.EGL_GREEN_SIZE,
+ 8,
+ EGL10.EGL_BLUE_SIZE,
+ 8,
+ EGL10.EGL_ALPHA_SIZE,
+ 8,
+ EGL10.EGL_DEPTH_SIZE,
+ 16,
+ EGL10.EGL_NONE,
+ )
val configs = arrayOfNulls(1)
val numConfigs = IntArray(1)
egl!!.eglChooseConfig(eglDisplay, configAttribs, configs, 1, numConfigs)
- val contextAttribs = intArrayOf(
- 0x3098, 2, // EGL_CONTEXT_CLIENT_VERSION
- EGL10.EGL_NONE
- )
+ val contextAttribs =
+ intArrayOf(
+ 0x3098,
+ 2, // EGL_CONTEXT_CLIENT_VERSION
+ EGL10.EGL_NONE,
+ )
- eglContext = egl!!.eglCreateContext(
- eglDisplay,
- configs[0],
- EGL10.EGL_NO_CONTEXT,
- contextAttribs
- )
+ eglContext =
+ egl!!.eglCreateContext(
+ eglDisplay,
+ configs[0],
+ EGL10.EGL_NO_CONTEXT,
+ contextAttribs,
+ )
- val surfaceAttribs = intArrayOf(
- EGL10.EGL_WIDTH, width,
- EGL10.EGL_HEIGHT, height,
- EGL10.EGL_NONE
- )
+ val surfaceAttribs =
+ intArrayOf(
+ EGL10.EGL_WIDTH,
+ width,
+ EGL10.EGL_HEIGHT,
+ height,
+ EGL10.EGL_NONE,
+ )
eglSurface = egl!!.eglCreatePbufferSurface(eglDisplay, configs[0], surfaceAttribs)
egl!!.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)
@@ -230,8 +246,8 @@ class Object3DToBitmapRenderer(
floatArrayOf(
parts[1].toFloat(),
parts[2].toFloat(),
- parts[3].toFloat()
- )
+ parts[3].toFloat(),
+ ),
)
}
@@ -241,8 +257,8 @@ class Object3DToBitmapRenderer(
floatArrayOf(
parts[1].toFloat(),
parts[2].toFloat(),
- parts[3].toFloat()
- )
+ parts[3].toFloat(),
+ ),
)
}
@@ -254,7 +270,9 @@ class Object3DToBitmapRenderer(
val normalIndex =
if (vertexData.size > 2 && vertexData[2].isNotEmpty()) {
vertexData[2].toInt() - 1
- } else vertexIndex
+ } else {
+ vertexIndex
+ }
// Add vertex
vertices.addAll(tempVertices[vertexIndex].toList())
@@ -283,11 +301,14 @@ class Object3DToBitmapRenderer(
vertices = createFloatBuffer(vertices.toFloatArray()),
normals = createFloatBuffer(normals.toFloatArray()),
indices = createShortBuffer(indices.toShortArray()),
- indexCount = indices.size
+ indexCount = indices.size,
)
}
- private fun calculateNormals(vertices: FloatArray, indices: ShortArray): FloatArray {
+ private fun calculateNormals(
+ vertices: FloatArray,
+ indices: ShortArray,
+ ): FloatArray {
val normals = FloatArray(vertices.size)
// Calculate face normals and accumulate vertex normals
@@ -303,11 +324,12 @@ class Object3DToBitmapRenderer(
val edge1 = floatArrayOf(v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2])
val edge2 = floatArrayOf(v3[0] - v1[0], v3[1] - v1[1], v3[2] - v1[2])
- val normal = floatArrayOf(
- edge1[1] * edge2[2] - edge1[2] * edge2[1],
- edge1[2] * edge2[0] - edge1[0] * edge2[2],
- edge1[0] * edge2[1] - edge1[1] * edge2[0]
- )
+ val normal =
+ floatArrayOf(
+ edge1[1] * edge2[2] - edge1[2] * edge2[1],
+ edge1[2] * edge2[0] - edge1[0] * edge2[2],
+ edge1[0] * edge2[1] - edge1[1] * edge2[0],
+ )
// Normalize
val length = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2])
@@ -340,21 +362,21 @@ class Object3DToBitmapRenderer(
return normals
}
- private fun createFloatBuffer(array: FloatArray): FloatBuffer {
- return ByteBuffer.allocateDirect(array.size * 4)
+ private fun createFloatBuffer(array: FloatArray): FloatBuffer =
+ ByteBuffer
+ .allocateDirect(array.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(array)
.apply { position(0) }
- }
- private fun createShortBuffer(array: ShortArray): ShortBuffer {
- return ByteBuffer.allocateDirect(array.size * 2)
+ private fun createShortBuffer(array: ShortArray): ShortBuffer =
+ ByteBuffer
+ .allocateDirect(array.size * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
.put(array)
.apply { position(0) }
- }
private fun setupShaders() {
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
@@ -372,14 +394,20 @@ class Object3DToBitmapRenderer(
colorHandle = GLES20.glGetUniformLocation(shaderProgram, "uColor")
}
- private fun loadShader(type: Int, shaderCode: String): Int {
+ private fun loadShader(
+ type: Int,
+ shaderCode: String,
+ ): Int {
val shader = GLES20.glCreateShader(type)
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
return shader
}
- private fun setupMatrices(width: Int, height: Int) {
+ private fun setupMatrices(
+ width: Int,
+ height: Int,
+ ) {
// Set up projection matrix
val ratio = width.toFloat() / height.toFloat()
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
@@ -391,7 +419,12 @@ class Object3DToBitmapRenderer(
Matrix.setIdentityM(modelMatrix, 0)
}
- private fun renderToBitmap(mesh: Mesh, width: Int, height: Int, color: FloatArray): Bitmap {
+ private fun renderToBitmap(
+ mesh: Mesh,
+ width: Int,
+ height: Int,
+ color: FloatArray,
+ ): Bitmap {
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f) // White background
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)
GLES20.glEnable(GLES20.GL_DEPTH_TEST)
@@ -420,7 +453,7 @@ class Object3DToBitmapRenderer(
GLES20.GL_TRIANGLES,
mesh.indexCount,
GLES20.GL_UNSIGNED_SHORT,
- mesh.indices
+ mesh.indices,
)
// Read pixels
@@ -432,7 +465,7 @@ class Object3DToBitmapRenderer(
height,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
- pixelBuffer
+ pixelBuffer,
)
// Convert to bitmap
@@ -453,7 +486,7 @@ class Object3DToBitmapRenderer(
eglDisplay,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_CONTEXT
+ EGL10.EGL_NO_CONTEXT,
)
eglSurface?.let { egl.eglDestroySurface(eglDisplay, it) }
eglContext?.let { egl.eglDestroyContext(eglDisplay, it) }
@@ -471,4 +504,4 @@ class Object3DToBitmapRenderer(
eglSurface = null
shaderProgram = 0
}
-}
\ No newline at end of file
+}
diff --git a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/OffscreenRenderer.kt b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/OffscreenRenderer.kt
index 39833432..399fb086 100644
--- a/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/OffscreenRenderer.kt
+++ b/modules/3d-opengl-renderer/src/main/java/com/tejpratapsingh/motionlib/openglrenderer/OffscreenRenderer.kt
@@ -10,47 +10,61 @@ import javax.microedition.khronos.egl.EGL10
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.egl.EGLContext
-class OffscreenRenderer(private val model: ObjModel) {
+class OffscreenRenderer(
+ private val model: ObjModel,
+) {
private val modelMatrix = FloatArray(16)
private val viewMatrix = FloatArray(16)
private val projectionMatrix = FloatArray(16)
private val mvpMatrix = FloatArray(16)
private var rotationAngle = 0f
- private val vertexShaderCode = """
+ private val vertexShaderCode =
+ """
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
void main() {
gl_Position = uMVPMatrix * vPosition;
}
- """.trimIndent()
+ """.trimIndent()
- private val fragmentShaderCode = """
+ private val fragmentShaderCode =
+ """
precision mediump float;
uniform vec4 vColor;
void main() {
gl_FragColor = vColor;
}
- """.trimIndent()
+ """.trimIndent()
fun setRotation(angleInDegrees: Float) {
rotationAngle = angleInDegrees
}
- fun renderOffscreen(width: Int = 512, height: Int = 512): Bitmap {
+ fun renderOffscreen(
+ width: Int = 512,
+ height: Int = 512,
+ ): Bitmap {
val egl = EGLContext.getEGL() as EGL10
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
egl.eglInitialize(display, null)
- val attribList = intArrayOf(
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 8,
- EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
- EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGL10.EGL_NONE
- )
+ val attribList =
+ intArrayOf(
+ EGL10.EGL_RED_SIZE,
+ 8,
+ EGL10.EGL_GREEN_SIZE,
+ 8,
+ EGL10.EGL_BLUE_SIZE,
+ 8,
+ EGL10.EGL_ALPHA_SIZE,
+ 8,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_PBUFFER_BIT,
+ EGL10.EGL_RENDERABLE_TYPE,
+ EGL14.EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE,
+ )
val configs = arrayOfNulls(1)
val numConfigs = IntArray(1)
@@ -60,11 +74,14 @@ class OffscreenRenderer(private val model: ObjModel) {
val attribList1 = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE)
val context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attribList1)
- val surfaceAttribs = intArrayOf(
- EGL10.EGL_WIDTH, width,
- EGL10.EGL_HEIGHT, height,
- EGL10.EGL_NONE
- )
+ val surfaceAttribs =
+ intArrayOf(
+ EGL10.EGL_WIDTH,
+ width,
+ EGL10.EGL_HEIGHT,
+ height,
+ EGL10.EGL_NONE,
+ )
val surface = egl.eglCreatePbufferSurface(display, config, surfaceAttribs)
egl.eglMakeCurrent(display, surface, surface, context)
@@ -101,7 +118,7 @@ class OffscreenRenderer(private val model: ObjModel) {
GLES20.GL_TRIANGLES,
model.indexCount,
GLES20.GL_UNSIGNED_SHORT,
- model.indexBuffer
+ model.indexBuffer,
)
GLES20.glDisableVertexAttribArray(posHandle)
@@ -116,7 +133,7 @@ class OffscreenRenderer(private val model: ObjModel) {
display,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_CONTEXT
+ EGL10.EGL_NO_CONTEXT,
)
egl.eglDestroySurface(display, surface)
egl.eglDestroyContext(display, context)
@@ -143,8 +160,11 @@ class OffscreenRenderer(private val model: ObjModel) {
}
}
- private fun loadShader(type: Int, code: String): Int {
- return GLES20.glCreateShader(type).also {
+ private fun loadShader(
+ type: Int,
+ code: String,
+ ): Int =
+ GLES20.glCreateShader(type).also {
GLES20.glShaderSource(it, code)
GLES20.glCompileShader(it)
val compiled = IntArray(1)
@@ -155,5 +175,4 @@ class OffscreenRenderer(private val model: ObjModel) {
throw RuntimeException("Could not compile shader $type: $log")
}
}
- }
-}
\ No newline at end of file
+}
diff --git a/modules/3d-opengl-renderer/src/test/java/com/tejpratapsingh/motionlib/openglrenderer/ExampleUnitTest.kt b/modules/3d-opengl-renderer/src/test/java/com/tejpratapsingh/motionlib/openglrenderer/ExampleUnitTest.kt
index c5683e0b..050516f6 100644
--- a/modules/3d-opengl-renderer/src/test/java/com/tejpratapsingh/motionlib/openglrenderer/ExampleUnitTest.kt
+++ b/modules/3d-opengl-renderer/src/test/java/com/tejpratapsingh/motionlib/openglrenderer/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/app/src/androidTest/java/com/tejpratapsingh/animator/ExampleInstrumentedTest.kt b/modules/app/src/androidTest/java/com/tejpratapsingh/animator/ExampleInstrumentedTest.kt
index f67b35b9..b5da221d 100644
--- a/modules/app/src/androidTest/java/com/tejpratapsingh/animator/ExampleInstrumentedTest.kt
+++ b/modules/app/src/androidTest/java/com/tejpratapsingh/animator/ExampleInstrumentedTest.kt
@@ -19,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tejpratapsingh.animator", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/activities/MotionPreviewActivity.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/activities/MotionPreviewActivity.kt
index b2f5660e..792b3831 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/activities/MotionPreviewActivity.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/activities/MotionPreviewActivity.kt
@@ -11,7 +11,6 @@ import com.tejpratapsingh.motionlib.activities.PreviewActivity
import com.tejpratapsingh.motionlib.core.motion.MotionVideoProducer
class MotionPreviewActivity : PreviewActivity() {
-
val video by lazy {
sampleMotionVideo(applicationContext)
}
@@ -21,11 +20,14 @@ class MotionPreviewActivity : PreviewActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(
- this, Manifest.permission.POST_NOTIFICATIONS
+ this,
+ Manifest.permission.POST_NOTIFICATIONS,
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
- this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0
+ this,
+ arrayOf(Manifest.permission.POST_NOTIFICATIONS),
+ 0,
)
}
}
@@ -33,7 +35,5 @@ class MotionPreviewActivity : PreviewActivity() {
SampleMotionWorker.startWork(applicationContext)
}
- override fun getMotionVideo(): MotionVideoProducer {
- return video
- }
-}
\ No newline at end of file
+ override fun getMotionVideo(): MotionVideoProducer = video
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/app/MyApplication.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/app/MyApplication.kt
index d640c23d..582f52b7 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/app/MyApplication.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/app/MyApplication.kt
@@ -12,4 +12,4 @@ class MyApplication : Application() {
// PyTorchImageProcessor.init(applicationContext)
TensorFlowImageProcessor.init(applicationContext)
}
-}
\ No newline at end of file
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationChannelType.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationChannelType.kt
index b6a15a87..5669c1a3 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationChannelType.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationChannelType.kt
@@ -9,18 +9,18 @@ enum class NotificationChannelType(
val channelId: String,
val channelNameResId: Int, // Changed to Int for resource ID
val channelDescriptionResId: Int, // Changed to Int for resource ID
- val importance: Int
+ val importance: Int,
) {
RENDERING_PROGRESS(
"render_progress_channel",
R.string.notification_channel_rendering_progress_name,
R.string.notification_channel_rendering_progress_description,
- NotificationManager.IMPORTANCE_LOW
+ NotificationManager.IMPORTANCE_LOW,
),
RENDERING_COMPLETED(
"render_completed_channel",
R.string.notification_channel_rendering_completed_name,
R.string.notification_channel_rendering_completed_description,
- NotificationManager.IMPORTANCE_DEFAULT
- );
+ NotificationManager.IMPORTANCE_DEFAULT,
+ ),
}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationFactory.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationFactory.kt
index 172628ac..a403e9c0 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationFactory.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/notification/NotificationFactory.kt
@@ -19,30 +19,30 @@ class NotificationFactory {
NotificationChannelType.entries.forEach { channelType ->
// Check if channel already exists to avoid re-creating unnecessarily
if (notificationManager.getNotificationChannel(channelType.channelId) == null) {
- val channel = NotificationChannel(
- channelType.channelId,
- context.getString(channelType.channelNameResId), // Use string resource
- channelType.importance
- ).apply {
- description =
- context.getString(channelType.channelDescriptionResId) // Use string resource
- }
+ val channel =
+ NotificationChannel(
+ channelType.channelId,
+ context.getString(channelType.channelNameResId), // Use string resource
+ channelType.importance,
+ ).apply {
+ description =
+ context.getString(channelType.channelDescriptionResId) // Use string resource
+ }
notificationManager.createNotificationChannel(channel)
}
}
}
}
- fun getRenderProgressNotification(
- context: Context
- ): NotificationCompat.Builder {
+ fun getRenderProgressNotification(context: Context): NotificationCompat.Builder {
val channelType = NotificationChannelType.RENDERING_PROGRESS
// Ensure channel is created.
// If you move channel creation to Application class, this explicit call might not be needed here.
createNotificationChannels(context) // Or a more optimized way to ensure channels are created
- return NotificationCompat.Builder(context, channelType.channelId)
+ return NotificationCompat
+ .Builder(context, channelType.channelId)
.setSmallIcon(R.drawable.ic_notification_burst)
.setContentTitle(context.getString(R.string.notification_render_progress_title)) // Use string resource
.setContentText(context.getString(R.string.notification_render_progress_text_starting)) // Use string resource
@@ -51,15 +51,14 @@ class NotificationFactory {
.setPriority(NotificationCompat.PRIORITY_LOW)
}
- fun getRenderCompleteNotification(
- context: Context
- ): NotificationCompat.Builder {
+ fun getRenderCompleteNotification(context: Context): NotificationCompat.Builder {
val channelType = NotificationChannelType.RENDERING_COMPLETED
// Ensure channel is created
createNotificationChannels(context) // Or a more optimized way to ensure channels are created
- return NotificationCompat.Builder(context, channelType.channelId)
+ return NotificationCompat
+ .Builder(context, channelType.channelId)
.setSmallIcon(R.drawable.ic_notification_burst)
.setContentTitle(context.getString(R.string.notification_render_complete_title)) // Use string resource
.setContentText(context.getString(R.string.notification_render_complete_text)) // Use string resource
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/presentation/SampleMotionVideo.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/presentation/SampleMotionVideo.kt
index 8e5ba35f..a75f8f47 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/presentation/SampleMotionVideo.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/presentation/SampleMotionVideo.kt
@@ -14,18 +14,21 @@ import kotlinx.coroutines.runBlocking
import java.io.File
fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
- val motionConfig = MotionConfig(
- aspectRatio = VideoAspectRatio.Ratio9x16_480, fps = 30
- )
+ val motionConfig =
+ MotionConfig(
+ aspectRatio = VideoAspectRatio.Ratio9x16_480,
+ fps = 30,
+ )
val assetManager = applicationContext.assets
val files = assetManager.list(RenaultCar.imageAssetSubFolder)
- val motionView: BaseContourMotionView = RenaultCar(
- context = applicationContext,
- startFrame = 1,
- endFrame = files?.size ?: 1
- )
+ val motionView: BaseContourMotionView =
+ RenaultCar(
+ context = applicationContext,
+ startFrame = 1,
+ endFrame = files?.size ?: 1,
+ )
// val motionView = ContourDevice(
// context = applicationContext, startFrame = 1, endFrame = motionConfig.fps * 4
@@ -39,7 +42,7 @@ fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
try {
httpClient.downloadFile(
file = file,
- url = "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/3d/be/de/3dbedeeb-4ef4-0b43-d23e-ed7b3ec0c034/mzaf_3312428321786187211.plus.aac.p.m4a"
+ url = "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/3d/be/de/3dbedeeb-4ef4-0b43-d23e-ed7b3ec0c034/mzaf_3312428321786187211.plus.aac.p.m4a",
)
} catch (e: Exception) {
e.printStackTrace()
@@ -47,14 +50,15 @@ fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
}
}
- val motionAudio = listOf(
- MotionAudio(
- file = file,
- delayFrame = motionView.startFrame,
- startFrame = motionView.startFrame,
- endFrame = motionView.endFrame
+ val motionAudio =
+ listOf(
+ MotionAudio(
+ file = file,
+ delayFrame = motionView.startFrame,
+ startFrame = motionView.startFrame,
+ endFrame = motionView.endFrame,
+ ),
)
- )
// val motionView = MotionOpenGlView(
// context = applicationContext,
@@ -101,9 +105,10 @@ fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
setBackgroundColor(Color.WHITE)
}*/
- return MotionVideoProducer.with(
- context = applicationContext,
- config = motionConfig,
- motionAudio = motionAudio,
- ).addMotionViewToSequence(motionView = motionView)
-}
\ No newline at end of file
+ return MotionVideoProducer
+ .with(
+ context = applicationContext,
+ config = motionConfig,
+ motionAudio = motionAudio,
+ ).addMotionViewToSequence(motionView = motionView)
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/ContourDevice.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/ContourDevice.kt
index b4d0e8ee..9e35b01e 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/ContourDevice.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/ContourDevice.kt
@@ -14,33 +14,38 @@ import com.tejpratapsingh.motionlib.core.extensions.toBitmap
import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
import com.tejpratapsingh.motionlib.ui.custom.text.TypeWriterTextView
-class ContourDevice(context: Context, startFrame: Int, endFrame: Int) :
- BaseContourMotionView(context, startFrame, endFrame) {
+class ContourDevice(
+ context: Context,
+ startFrame: Int,
+ endFrame: Int,
+) : BaseContourMotionView(context, startFrame, endFrame) {
+ private val typeWriterWriterTextView: TypeWriterTextView =
+ TypeWriterTextView(
+ context = context,
+ text = "Hello,\nWelcome to the future",
+ startFrame = startFrame,
+ endFrame = endFrame,
+ ).apply {
+ setBackgroundColor(Color.WHITE)
- private val typeWriterWriterTextView: TypeWriterTextView = TypeWriterTextView(
- context = context,
- text = "Hello,\nWelcome to the future",
- startFrame = startFrame,
- endFrame = endFrame
- ).apply {
- setBackgroundColor(Color.WHITE)
-
- textView.textSize = 18f
- textView.gravity = Gravity.CENTER
- }
+ textView.textSize = 18f
+ textView.gravity = Gravity.CENTER
+ }
init {
typeWriterWriterTextView.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- },
- y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
)
contourHeightOf {
@@ -54,25 +59,26 @@ class ContourDevice(context: Context, startFrame: Int, endFrame: Int) :
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val backgroundColor: Int = MotionInterpolator.interpolateColorForRange(
- Interpolators(Easings.LINEAR),
- frame,
- Pair(startFrame, endFrame),
- Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt())
- )
+ val backgroundColor: Int =
+ MotionInterpolator.interpolateColorForRange(
+ Interpolators(Easings.LINEAR),
+ frame,
+ Pair(startFrame, endFrame),
+ Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt()),
+ )
typeWriterWriterTextView.setBackgroundColor(
- backgroundColor
+ backgroundColor,
)
typeWriterWriterTextView.textView.setTextColor(
MotionInterpolator.getComplementaryColor(
- backgroundColor
- )
+ backgroundColor,
+ ),
)
return this
}
override fun getViewBitmap(): Bitmap = this.toBitmap()
-}
\ No newline at end of file
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/MotionVideoContainer.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/MotionVideoContainer.kt
index e72420e2..a0866477 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/MotionVideoContainer.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/ui/view/MotionVideoContainer.kt
@@ -20,113 +20,128 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-
-class MotionVideoContainer(context: Context, motionVideoProducer: MotionVideoProducer) :
- ContourLayout(context) {
-
+class MotionVideoContainer(
+ context: Context,
+ motionVideoProducer: MotionVideoProducer,
+) : ContourLayout(context) {
private val TAG = "MotionVideoContainer"
- private val toolbar: Toolbar = Toolbar(context).apply {
- title = "Video"
- subtitle = "Create New"
- setBackgroundColor(Color.CYAN)
- }
-
- private val videoPlayer: MotionVideoPlayer = MotionVideoPlayer(
- context, motionVideoProducer
- )
-
- private val exportVideo: Button = Button(context).apply {
- text = context.getString(R.string.export_video)
+ private val toolbar: Toolbar =
+ Toolbar(context).apply {
+ title = "Video"
+ subtitle = "Create New"
+ setBackgroundColor(Color.CYAN)
+ }
- val scope = CoroutineScope(
- Dispatchers.Main + SupervisorJob()
+ private val videoPlayer: MotionVideoPlayer =
+ MotionVideoPlayer(
+ context,
+ motionVideoProducer,
)
- setOnClickListener {
- visibility = GONE
+ private val exportVideo: Button =
+ Button(context).apply {
+ text = context.getString(R.string.export_video)
- scope.launch {
- val uri: Uri = generateVideo(
- motionVideoProducer = motionVideoProducer,
- progressListener = { progress, bitmap ->
- scope.launch {
- Log.d(TAG, "Progress: $progress")
- videoPlayer.seekBar.progress = progress
- }
- }
+ val scope =
+ CoroutineScope(
+ Dispatchers.Main + SupervisorJob(),
)
- visibility = VISIBLE
- val shareIntent = Intent().apply {
- action = Intent.ACTION_SEND
- type = "video/*"
- putExtra(Intent.EXTRA_STREAM, uri)
+ setOnClickListener {
+ visibility = GONE
+
+ scope.launch {
+ val uri: Uri =
+ generateVideo(
+ motionVideoProducer = motionVideoProducer,
+ progressListener = { progress, bitmap ->
+ scope.launch {
+ Log.d(TAG, "Progress: $progress")
+ videoPlayer.seekBar.progress = progress
+ }
+ },
+ )
+
+ visibility = VISIBLE
+ val shareIntent =
+ Intent().apply {
+ action = Intent.ACTION_SEND
+ type = "video/*"
+ putExtra(Intent.EXTRA_STREAM, uri)
+
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ context.startActivity(Intent.createChooser(shareIntent, "Share File"))
}
-
- context.startActivity(Intent.createChooser(shareIntent, "Share File"))
}
}
- }
init {
toolbar.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- },
- y = topTo {
- parent.top()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ },
)
exportVideo.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- },
- y = bottomTo {
- parent.bottom()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ bottomTo {
+ parent.bottom()
+ },
)
videoPlayer.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- },
- y = topTo {
- toolbar.bottom()
- }.bottomTo {
- exportVideo.top()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ toolbar.bottom()
+ }.bottomTo {
+ exportVideo.top()
+ },
)
}
suspend fun generateVideo(
motionVideoProducer: MotionVideoProducer,
- progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?
+ progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?,
): Uri =
withContext(Dispatchers.IO) {
- val fileToShare = motionVideoProducer.produceVideo(
- context = context,
- outputFile = File.createTempFile(
- "out",
- ".mp4",
- context.filesDir
- ),
- progressListener = progressListener
- )
+ val fileToShare =
+ motionVideoProducer.produceVideo(
+ context = context,
+ outputFile =
+ File.createTempFile(
+ "out",
+ ".mp4",
+ context.filesDir,
+ ),
+ progressListener = progressListener,
+ )
FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
- fileToShare
+ fileToShare,
)
}
-}
\ 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 d573fcdc..820b307e 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
@@ -12,16 +12,19 @@ import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
import java.io.IOException
import java.io.InputStream
-class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
- BaseContourMotionView(context, startFrame, endFrame) {
-
+class RenaultCar(
+ context: Context,
+ startFrame: Int,
+ endFrame: Int,
+) : BaseContourMotionView(context, startFrame, endFrame) {
companion object {
const val imageAssetSubFolder = "renault_kiger_bg"
}
- private val imageView: ImageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
+ private val imageView: ImageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
private val assetManager = context.assets
@@ -31,16 +34,18 @@ class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
init {
imageView.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- },
- y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
)
contourHeightOf {
@@ -54,15 +59,16 @@ class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val backgroundColor: Int = MotionInterpolator.interpolateColorForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt())
- )
+ val backgroundColor: Int =
+ MotionInterpolator.interpolateColorForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt()),
+ )
setBackgroundColor(
- backgroundColor
+ backgroundColor,
)
// Determine which image to show based on the current frame
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/utils/Timer.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/utils/Timer.kt
index dc5b8a27..63dfb4a6 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/utils/Timer.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/utils/Timer.kt
@@ -6,8 +6,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-class Timer() {
-
+class Timer {
companion object {
private const val TAG = "Timer"
}
@@ -18,7 +17,7 @@ class Timer() {
private fun startCoroutineTimer(
delayMillis: Long = 0,
repeatMillis: Long = 0,
- action: () -> Unit
+ action: () -> Unit,
) = scope.launch(Dispatchers.IO) {
delay(delayMillis)
if (repeatMillis > 0) {
@@ -30,4 +29,4 @@ class Timer() {
action()
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/app/src/main/java/com/tejpratapsingh/animator/worker/SampleMotionWorker.kt b/modules/app/src/main/java/com/tejpratapsingh/animator/worker/SampleMotionWorker.kt
index c6e523d9..c7018f94 100644
--- a/modules/app/src/main/java/com/tejpratapsingh/animator/worker/SampleMotionWorker.kt
+++ b/modules/app/src/main/java/com/tejpratapsingh/animator/worker/SampleMotionWorker.kt
@@ -29,9 +29,10 @@ import java.net.URLConnection
import java.util.Locale
import java.util.UUID
-class SampleMotionWorker(private val appContext: Context, parameters: WorkerParameters) :
- MotionWorker(appContext, parameters) {
-
+class SampleMotionWorker(
+ private val appContext: Context,
+ parameters: WorkerParameters,
+) : MotionWorker(appContext, parameters) {
private val notificationManager = NotificationManagerCompat.from(appContext)
private val progressNotificationBuilder: NotificationCompat.Builder by lazy {
@@ -44,50 +45,54 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
private fun createForegroundInfo(
progressNotificationId: Int,
- notification: Notification
- ): ForegroundInfo {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ notification: Notification,
+ ): ForegroundInfo =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
ForegroundInfo(
progressNotificationId,
notification,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,
)
} else {
ForegroundInfo(progressNotificationId, notification)
}
- }
override suspend fun getForegroundInfo(): ForegroundInfo {
// Create the notification for the foreground service
- val notification = progressNotificationBuilder
- .setContentTitle("Rendering Video...") // Initial title
- .setProgress(0, 0, true) // Indeterminate progress initially
- .setOngoing(true)
- .build()
+ val notification =
+ progressNotificationBuilder
+ .setContentTitle("Rendering Video...") // Initial title
+ .setProgress(0, 0, true) // Indeterminate progress initially
+ .setOngoing(true)
+ .build()
return createForegroundInfo(progressNotificationId, notification)
}
- override fun getMotionVideo(inputData: Data): MotionVideoProducer {
- return sampleMotionVideo(appContext)
- }
+ override fun getMotionVideo(inputData: Data): MotionVideoProducer = sampleMotionVideo(appContext)
- override fun onProgress(totalFrames: Int, currentProgress: Int, bitmap: Bitmap) {
+ override fun onProgress(
+ totalFrames: Int,
+ currentProgress: Int,
+ bitmap: Bitmap,
+ ) {
Log.d(TAG, "onProgress: $currentProgress / $totalFrames")
val percentage = (currentProgress.toDouble() / totalFrames) * 100
- val progressText = String.format(
- Locale.getDefault(),
- "%d/%d frames completed",
- currentProgress,
- totalFrames
- )
+ val progressText =
+ String.format(
+ Locale.getDefault(),
+ "%d/%d frames completed",
+ currentProgress,
+ totalFrames,
+ )
val contentText = String.format(Locale.getDefault(), "%.0f%%", percentage)
- val notification = progressNotificationBuilder
- .setProgress(totalFrames, currentProgress, false)
- .setSubText(progressText)
- .setContentText(contentText)
- .build()
+ val notification =
+ progressNotificationBuilder
+ .setProgress(totalFrames, currentProgress, false)
+ .setSubText(progressText)
+ .setContentText(contentText)
+ .build()
updateNotification(progressNotificationId, notification)
@@ -106,25 +111,24 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
val intentOpenFile = Intent(Intent.ACTION_VIEW)
val pendingOpenFileIntent = createPendingIntentFor(intentOpenFile, videoFile)
- val completedNotification = completedNotificationBuilder
- .setContentTitle("Render Complete")
- .setContentText("Video ready: ${videoFile.name}")
- .addAction(
- NotificationCompat.Action(
- android.R.drawable.ic_menu_share, // Consider using a custom icon
- "Share Video",
- pendingShareIntent
- )
- )
- .addAction(
- NotificationCompat.Action(
- android.R.drawable.ic_menu_share, // Consider using a custom icon
- "Open Video",
- pendingOpenFileIntent
- )
- )
- .setAutoCancel(true) // Dismiss notification when tapped (if no content intent set)
- .build()
+ val completedNotification =
+ completedNotificationBuilder
+ .setContentTitle("Render Complete")
+ .setContentText("Video ready: ${videoFile.name}")
+ .addAction(
+ NotificationCompat.Action(
+ android.R.drawable.ic_menu_share, // Consider using a custom icon
+ "Share Video",
+ pendingShareIntent,
+ ),
+ ).addAction(
+ NotificationCompat.Action(
+ android.R.drawable.ic_menu_share, // Consider using a custom icon
+ "Open Video",
+ pendingOpenFileIntent,
+ ),
+ ).setAutoCancel(true) // Dismiss notification when tapped (if no content intent set)
+ .build()
updateNotification(completedNotificationId, completedNotification)
}
@@ -132,7 +136,10 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
@Volatile
private var lastNotificationUpdateTime = 0L
- private fun updateNotification(notificationId: Int, notification: Notification) {
+ private fun updateNotification(
+ notificationId: Int,
+ notification: Notification,
+ ) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastNotificationUpdateTime < 500) {
return
@@ -141,7 +148,7 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
if (ActivityCompat.checkSelfPermission(
appContext,
- Manifest.permission.POST_NOTIFICATIONS
+ Manifest.permission.POST_NOTIFICATIONS,
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(notificationId, notification)
@@ -152,15 +159,19 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
}
}
- private fun createPendingIntentFor(intent: Intent, videoFile: File): PendingIntent {
- val apkURI: Uri = FileProvider.getUriForFile(
- appContext,
- "${appContext.packageName}.fileprovider",
- videoFile
- )
+ private fun createPendingIntentFor(
+ intent: Intent,
+ videoFile: File,
+ ): PendingIntent {
+ val apkURI: Uri =
+ FileProvider.getUriForFile(
+ appContext,
+ "${appContext.packageName}.fileprovider",
+ videoFile,
+ )
intent.setDataAndType(
apkURI,
- URLConnection.guessContentTypeFromName(videoFile.name)
+ URLConnection.guessContentTypeFromName(videoFile.name),
)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(Intent.EXTRA_STREAM, apkURI)
@@ -172,7 +183,7 @@ class SampleMotionWorker(private val appContext: Context, parameters: WorkerPara
appContext,
0, // requestCode, consider making this unique if you have many such intents
intent,
- pendingShareIntentFlags
+ pendingShareIntentFlags,
)
}
diff --git a/modules/app/src/test/java/com/tejpratapsingh/animator/ExampleUnitTest.kt b/modules/app/src/test/java/com/tejpratapsingh/animator/ExampleUnitTest.kt
index 2b5a12c5..03b29910 100644
--- a/modules/app/src/test/java/com/tejpratapsingh/animator/ExampleUnitTest.kt
+++ b/modules/app/src/test/java/com/tejpratapsingh/animator/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionAudio.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionAudio.kt
index d219356c..aceccb93 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionAudio.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionAudio.kt
@@ -12,7 +12,7 @@ import java.io.File
*/
data class MotionAudio(
val file: File,
- val startFrame: Int, // trim start frame
- val endFrame: Int, // trim end frame
- val delayFrame: Int // delay in frames
-)
\ No newline at end of file
+ val startFrame: Int, // trim start frame
+ val endFrame: Int, // trim end frame
+ val delayFrame: Int, // delay in frames
+)
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionConfig.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionConfig.kt
index 055b25f9..c387857e 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionConfig.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionConfig.kt
@@ -8,11 +8,11 @@ data object MotionConfig {
operator fun invoke(
aspectRatio: VideoAspectRatio = VideoAspectRatio.Ratio9x16_480,
fps: Int = 24,
- outputQuality: Int = 100
+ outputQuality: Int = 100,
): MotionConfig {
this.aspectRatio = aspectRatio
this.fps = fps
this.outputQuality = outputQuality
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionEffect.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionEffect.kt
index 2f63baad..5803f23f 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionEffect.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionEffect.kt
@@ -4,4 +4,4 @@ interface MotionEffect : OnMotionFrameListener {
val motionView: MotionView
val startFrame: Int
val endFrame: Int
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionPlugin.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionPlugin.kt
index a7f326d1..98b46cc9 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionPlugin.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionPlugin.kt
@@ -4,4 +4,4 @@ import android.graphics.Bitmap
interface MotionPlugin {
fun apply(input: Bitmap): Bitmap
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionView.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionView.kt
index a70801de..37dbb21d 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionView.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/MotionView.kt
@@ -11,4 +11,4 @@ interface MotionView : OnMotionFrameListener {
val effects: List
fun getViewBitmap(): Bitmap
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/OnMotionFrameListener.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/OnMotionFrameListener.kt
index 60855de9..fef471dd 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/OnMotionFrameListener.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/OnMotionFrameListener.kt
@@ -2,4 +2,4 @@ package com.tejpratapsingh.motionlib.core
fun interface OnMotionFrameListener {
fun forFrame(frame: Int): MotionView
-}
\ No newline at end of file
+}
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 d2c961e9..cd031136 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,42 +1,55 @@
package com.tejpratapsingh.motionlib.core
-sealed class VideoAspectRatio(val width: Int, val height: Int, val label: String) {
-
+sealed class VideoAspectRatio(
+ val width: Int,
+ val height: Int,
+ val label: String,
+) {
// Square
data object Ratio1x1_480 : VideoAspectRatio(480, 480, "1:1 SD")
+
data object Ratio1x1_720 : VideoAspectRatio(720, 720, "1:1 HD")
+
data object Ratio1x1_1080 : VideoAspectRatio(1080, 1080, "1:1 Full HD")
// Classic TV 4:3
data object Ratio4x3_480 : VideoAspectRatio(640, 480, "4:3 SD")
+
data object Ratio4x3_576 : VideoAspectRatio(768, 576, "4:3 PAL SD")
+
data object Ratio4x3_720 : VideoAspectRatio(960, 720, "4:3 HD")
// Widescreen 16:9
data object Ratio16x9_480 : VideoAspectRatio(854, 480, "16:9 SD")
+
data object Ratio16x9_720 : VideoAspectRatio(1280, 720, "16:9 HD")
+
data object Ratio16x9_1080 : VideoAspectRatio(1920, 1080, "16:9 Full HD")
+
data object Ratio16x9_1440 : VideoAspectRatio(2560, 1440, "16:9 2K")
+
data object Ratio16x9_2160 : VideoAspectRatio(3840, 2160, "16:9 4K")
// Portrait (for mobile)
data object Ratio9x16_480 : VideoAspectRatio(480, 854, "9:16 SD")
+
data object Ratio9x16_720 : VideoAspectRatio(720, 1280, "9:16 HD")
+
data object Ratio9x16_1080 : VideoAspectRatio(1080, 1920, "9:16 Full HD")
// Cinema wide 21:9
data object Ratio21x9_1080 : VideoAspectRatio(2520, 1080, "21:9 Full HD")
+
data object Ratio21x9_2160 : VideoAspectRatio(5120, 2160, "21:9 5K")
// Custom ratio with any pixel size
data class Custom(
val customWidth: Int,
val customHeight: Int,
- val customLabel: String = "Custom"
- ) :
- VideoAspectRatio(customWidth, customHeight, customLabel)
+ val customLabel: String = "Custom",
+ ) : VideoAspectRatio(customWidth, customHeight, customLabel)
- fun ratioString(): String = "${width}:${height}"
+ fun ratioString(): String = "$width:$height"
fun aspect(): Float = width.toFloat() / height.toFloat()
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoProducerAdapter.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoProducerAdapter.kt
index 2fea8f00..db875b3b 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoProducerAdapter.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/VideoProducerAdapter.kt
@@ -12,6 +12,6 @@ interface VideoProducerAdapter {
motionAudio: List,
totalFrames: Int,
outputFile: File,
- progressListener: ((Int, Bitmap) -> Unit)?
+ progressListener: ((Int, Bitmap) -> Unit)?,
): File
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/AssetExtension.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/AssetExtension.kt
index bc6ee0ef..851090f1 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/AssetExtension.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/AssetExtension.kt
@@ -1,6 +1,5 @@
package com.tejpratapsingh.motionlib.core.extensions
-
import android.content.Context
import android.net.Uri
import java.io.File
@@ -41,4 +40,4 @@ fun Context.getFileFromAsset(assetFilePath: String): File {
}
return outFile // For internal use only
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/BitmapExtension.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/BitmapExtension.kt
index 304f564e..a23a73ae 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/BitmapExtension.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/BitmapExtension.kt
@@ -9,7 +9,7 @@ fun Bitmap.compressToBitmap(quality: Int): Bitmap {
val stream = ByteArrayOutputStream()
/*
- **** reference source developer.android.com ***
+ **** reference source developer.android.com ***
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
Write a compressed version of the bitmap to the specified outputstream.
@@ -37,7 +37,7 @@ fun Bitmap.compressToBitmap(quality: Int): Bitmap {
Bitmap.CompressFormat JPEG
Bitmap.CompressFormat PNG
Bitmap.CompressFormat WEBP
- */
+ */
// Compress the bitmap with JPEG format and quality 50%
this.compress(Bitmap.CompressFormat.JPEG, quality, stream)
@@ -46,4 +46,4 @@ fun Bitmap.compressToBitmap(quality: Int): Bitmap {
// Finally, return the compressed bitmap
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ContextExtensions.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ContextExtensions.kt
index 4ce8da8b..3111ef02 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ContextExtensions.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ContextExtensions.kt
@@ -11,17 +11,22 @@ fun Context.loadBitmapsFromDirectory(dirName: String): List {
return emptyList()
}
- return dir.listFiles { file ->
- file.extension.lowercase() in listOf("png", "jpg", "jpeg", "webp")
- }?.sortedBy { file ->
- // Extract digits from filename, default to 0 if no digits found
- file.nameWithoutExtension.filter { it.isDigit() }.toIntOrNull() ?: 0
- }?.mapNotNull { file ->
- BitmapFactory.decodeFile(file.absolutePath)
- } ?: emptyList()
+ return dir
+ .listFiles { file ->
+ file.extension.lowercase() in listOf("png", "jpg", "jpeg", "webp")
+ }?.sortedBy { file ->
+ // Extract digits from filename, default to 0 if no digits found
+ file.nameWithoutExtension.filter { it.isDigit() }.toIntOrNull() ?: 0
+ }?.mapNotNull { file ->
+ BitmapFactory.decodeFile(file.absolutePath)
+ } ?: emptyList()
}
-fun Context.saveBitmapToCacheFolder(bitmap: Bitmap, subDirName: String, fileName: String) {
+fun Context.saveBitmapToCacheFolder(
+ bitmap: Bitmap,
+ subDirName: String,
+ fileName: String,
+) {
val cacheSubDir = File(this.cacheDir, subDirName)
if (!cacheSubDir.exists()) {
cacheSubDir.mkdirs()
@@ -30,4 +35,4 @@ fun Context.saveBitmapToCacheFolder(bitmap: Bitmap, subDirName: String, fileName
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) // Using PNG as per your pattern
}
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/KtorExtension.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/KtorExtension.kt
index c647a57c..83a64747 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/KtorExtension.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/KtorExtension.kt
@@ -19,17 +19,22 @@ import java.io.File
import java.io.IOException // For more specific IO exceptions
// Custom exception for better error handling if desired
-class DownloadException(message: String, cause: Throwable? = null) : IOException(message, cause)
+class DownloadException(
+ message: String,
+ cause: Throwable? = null,
+) : IOException(message, cause)
suspend fun HttpClient.downloadFile(
file: File,
- url: String
+ url: String,
): File {
try {
- val response: HttpResponse = this.request { // Changed 'call' to 'response' for clarity
- url(url)
- method = HttpMethod.Get
- }
+ val response: HttpResponse =
+ this.request {
+ // Changed 'call' to 'response' for clarity
+ url(url)
+ method = HttpMethod.Get
+ }
if (!response.status.isSuccess()) {
throw DownloadException("Error downloading file from $url: HTTP ${response.status}")
@@ -51,7 +56,7 @@ suspend fun HttpClient.downloadFile(
// Catch specific IOExceptions during file writing
throw DownloadException(
"Failed to write downloaded file to ${file.path}: ${e.message}",
- e
+ e,
)
}
}
@@ -62,17 +67,16 @@ suspend fun HttpClient.downloadFile(
// Catch other potential exceptions (e.g., Ktor client exceptions)
throw DownloadException(
"An unexpected error occurred during download from $url: ${e.message}",
- e
+ e,
)
}
}
-suspend fun HttpClient.fetchBitmap(url: String): Bitmap? {
- return try {
+suspend fun HttpClient.fetchBitmap(url: String): Bitmap? =
+ try {
val bytes: ByteArray = get(url).body()
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
} catch (e: Exception) {
e.printStackTrace()
null
}
-}
\ No newline at end of file
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/StringExtension.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/StringExtension.kt
index 8343861d..512f9974 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/StringExtension.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/StringExtension.kt
@@ -6,4 +6,4 @@ fun String.md5(): String {
val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
return bytes.joinToString("") { "%02x".format(it) }
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ViewExtension.kt b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ViewExtension.kt
index 5e32164f..11038265 100644
--- a/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ViewExtension.kt
+++ b/modules/core/src/main/java/com/tejpratapsingh/motionlib/core/extensions/ViewExtension.kt
@@ -6,22 +6,22 @@ import android.view.View
import androidx.core.graphics.createBitmap
fun View.toBitmap(): Bitmap {
- //Get the dimensions of the view so we can re-layout the view at its current size
- //and create a bitmap of the same size
+ // Get the dimensions of the view so we can re-layout the view at its current size
+ // and create a bitmap of the same size
val width = this.width
val height = this.height
val measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY)
val measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
- //Cause the view to re-layout
+ // Cause the view to re-layout
this.measure(measuredWidth, measuredHeight)
this.layout(0, 0, this.measuredWidth, this.measuredHeight)
- //Create a bitmap backed Canvas to draw the view into
+ // Create a bitmap backed Canvas to draw the view into
val b = createBitmap(width, height)
val c = Canvas(b)
- //Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
+ // Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
this.draw(c)
return b
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/ExampleUnitTest.kt b/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/ExampleUnitTest.kt
index 87bfbd39..a65fea52 100644
--- a/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/ExampleUnitTest.kt
+++ b/modules/core/src/test/java/com/tejpratapsingh/motionlib/core/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/FfmpegVideoProducerAdapter.kt b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/FfmpegVideoProducerAdapter.kt
index 3baef27b..6e457615 100644
--- a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/FfmpegVideoProducerAdapter.kt
+++ b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/FfmpegVideoProducerAdapter.kt
@@ -15,7 +15,6 @@ import java.io.File
import java.util.Locale
class FfmpegVideoProducerAdapter : VideoProducerAdapter {
-
companion object {
private const val TAG = "FfmpegVideoProducerAdap"
}
@@ -29,7 +28,7 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
motionAudio: List,
totalFrames: Int,
outputFile: File,
- progressListener: ((Int, Bitmap) -> Unit)?
+ progressListener: ((Int, Bitmap) -> Unit)?,
): File {
if (outputFile.exists()) {
outputFile.delete()
@@ -44,13 +43,18 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
for (i in 1..totalFrames) {
Log.d(TAG, "produceVideo: frame $i")
- val frameBitmap: Bitmap = motionComposerView.forFrame(i).getViewBitmap()
- .compressToBitmap(motionConfig.outputQuality)
+ val frameBitmap: Bitmap =
+ motionComposerView
+ .forFrame(i)
+ .getViewBitmap()
+ .compressToBitmap(motionConfig.outputQuality)
// It's good practice to handle potential IOExceptions when saving files
try {
context.saveBitmapToCacheFolder(
- frameBitmap, subDirName, String.format(Locale.getDefault(), "%05d.png", i)
+ frameBitmap,
+ subDirName,
+ String.format(Locale.getDefault(), "%05d.png", i),
)
} catch (e: Exception) {
Log.e(TAG, "Error saving frame $i: ${e.message}", e)
@@ -75,14 +79,15 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
// -r: Output framerate (often the same as input, but can be different)
// val query = "-y -framerate ${motionConfig.fps} -start_number 1 -i \"$inputPattern\" -c:v libx264 -pix_fmt yuv420p -r ${motionConfig.fps} \"${outputFile.path}\""
- val query = buildFfmpegCommand(
- inputPattern = inputPattern,
- fps = motionConfig.fps,
- outputFile = outputFile,
- audioTracks = motionAudio,
- startNumber = 1,
- mixAudio = true // Change to false if you want separate audio tracks
- ).joinToString(" ")
+ val query =
+ buildFfmpegCommand(
+ inputPattern = inputPattern,
+ fps = motionConfig.fps,
+ outputFile = outputFile,
+ audioTracks = motionAudio,
+ startNumber = 1,
+ mixAudio = true, // Change to false if you want separate audio tracks
+ ).joinToString(" ")
Log.d(TAG, "Executing FFmpeg query: $query")
val session = FFmpegKit.execute(query)
@@ -108,12 +113,12 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
}
fun buildFfmpegCommand(
- inputPattern: String, // e.g. "/sdcard/frames/frame_%d.png"
+ inputPattern: String, // e.g. "/sdcard/frames/frame_%d.png"
fps: Int,
outputFile: File,
audioTracks: List,
startNumber: Int = 1,
- mixAudio: Boolean = true // true = mix tracks, false = keep separate
+ mixAudio: Boolean = true, // true = mix tracks, false = keep separate
): List {
val command = mutableListOf()
@@ -121,10 +126,13 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
command.addAll(
listOf(
"-y",
- "-framerate", fps.toString(),
- "-start_number", startNumber.toString(),
- "-i", inputPattern
- )
+ "-framerate",
+ fps.toString(),
+ "-start_number",
+ startNumber.toString(),
+ "-i",
+ inputPattern,
+ ),
)
// Add audio inputs
@@ -142,9 +150,9 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
val label = "a${index + 1}"
filterParts.add(
- "[${index + 1}:a]atrim=start=${startSec}:end=${endSec}," +
- "asetpts=PTS-STARTPTS," +
- "adelay=${delayMs}|${delayMs}[$label]"
+ "[${index + 1}:a]atrim=start=$startSec:end=$endSec," +
+ "asetpts=PTS-STARTPTS," +
+ "adelay=$delayMs|$delayMs[$label]",
)
}
@@ -167,13 +175,13 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
"-r",
fps.toString(),
"-shortest",
- outputFile.absolutePath
- )
+ outputFile.absolutePath,
+ ),
)
} else {
// Keep tracks separate, no filter_complex
command.addAll(
- listOf("-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", fps.toString())
+ listOf("-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", fps.toString()),
)
// Map video and each audio
@@ -189,7 +197,8 @@ class FfmpegVideoProducerAdapter : VideoProducerAdapter {
return command
}
- fun frameToSeconds(frame: Int, fps: Int): Double {
- return frame.toDouble() / fps.toDouble()
- }
+ fun frameToSeconds(
+ frame: Int,
+ fps: Int,
+ ): Double = frame.toDouble() / fps.toDouble()
}
diff --git a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/utils/FFMpegExtensions.kt b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/utils/FFMpegExtensions.kt
index fb118ce1..c21a3bbd 100644
--- a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/utils/FFMpegExtensions.kt
+++ b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/utils/FFMpegExtensions.kt
@@ -6,7 +6,9 @@ import com.arthenica.ffmpegkit.ReturnCode
import java.io.File
fun extractFramesFromVideo(
- context: Context, videoFile: File, outputDirName: String = "frames"
+ context: Context,
+ videoFile: File,
+ outputDirName: String = "frames",
): String {
val outputDir = File(context.cacheDir, outputDirName)
if (!outputDir.exists()) outputDir.mkdirs()
@@ -28,4 +30,4 @@ fun extractFramesFromVideo(
}
return outputDirName
-}
\ No newline at end of file
+}
diff --git a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/video/FFMpegVideoFrameView.kt b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/video/FFMpegVideoFrameView.kt
index 4cf42b35..4e07df2a 100644
--- a/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/video/FFMpegVideoFrameView.kt
+++ b/modules/ffmpeg-motion-ext/src/main/java/com/tejpratapsingh/motionlib/ffmpeg/video/FFMpegVideoFrameView.kt
@@ -16,21 +16,25 @@ class FFMpegVideoFrameView(
val videoFile: File,
override val startFrame: Int,
override val endFrame: Int,
- override val loop: Pair = Pair(0, 0)
-) : FrameLayout(context), MotionView {
-
+ override val loop: Pair = Pair(0, 0),
+) : FrameLayout(context),
+ MotionView {
override val effects: List = emptyList()
- val imageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.CENTER_CROP
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
- }
-
- private val videoBitmaps = extractFramesFromVideo(
- context = context, videoFile = videoFile, outputDirName = videoFile.name.md5()
- ).let {
- context.loadBitmapsFromDirectory(it)
- }
+ val imageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.CENTER_CROP
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
+ }
+
+ private val videoBitmaps =
+ extractFramesFromVideo(
+ context = context,
+ videoFile = videoFile,
+ outputDirName = videoFile.name.md5(),
+ ).let {
+ context.loadBitmapsFromDirectory(it)
+ }
init {
addView(imageView)
@@ -41,10 +45,10 @@ class FFMpegVideoFrameView(
override fun forFrame(frame: Int): MotionView {
currentFrameBitmap = videoBitmaps.getOrNull(frame - startFrame) ?: videoBitmaps.last()
imageView.setImageBitmap(
- currentFrameBitmap
+ currentFrameBitmap,
)
return this
}
override fun getViewBitmap(): Bitmap = currentFrameBitmap
-}
\ No newline at end of file
+}
diff --git a/modules/ffmpeg-motion-ext/src/test/java/com/tejpratapsingh/motionlib/ffmpeg/ExampleUnitTest.kt b/modules/ffmpeg-motion-ext/src/test/java/com/tejpratapsingh/motionlib/ffmpeg/ExampleUnitTest.kt
index bb543cbc..4c79cd90 100644
--- a/modules/ffmpeg-motion-ext/src/test/java/com/tejpratapsingh/motionlib/ffmpeg/ExampleUnitTest.kt
+++ b/modules/ffmpeg-motion-ext/src/test/java/com/tejpratapsingh/motionlib/ffmpeg/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt b/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt
index 6834f048..3333ff22 100644
--- a/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt
+++ b/modules/ivi-demo/src/androidTest/java/com/tejpratapsingh/ivi_demo/ExampleInstrumentedTest.kt
@@ -19,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tejpratapsingh.ivi_demo", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt
index 696db9d2..d0354e97 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/MainActivity.kt
@@ -11,23 +11,25 @@ import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
import com.tejpratapsingh.motionlib.core.motion.MotionVideoProducer
class MainActivity : PreviewActivity() {
-
val video by lazy {
- MotionVideoProducer.with(
- context = applicationContext,
- config = motionConfig,
- ).addMotionViewToSequence(motionView = motionView)
+ MotionVideoProducer
+ .with(
+ context = applicationContext,
+ config = motionConfig,
+ ).addMotionViewToSequence(motionView = motionView)
}
- val motionConfig = MotionConfig(
- aspectRatio = VideoAspectRatio.Ratio16x9_480, fps = 30
- )
+ val motionConfig =
+ MotionConfig(
+ aspectRatio = VideoAspectRatio.Ratio16x9_480,
+ fps = 30,
+ )
val motionView: BaseContourMotionView by lazy {
RenaultCar(
context = applicationContext,
startFrame = 1,
- endFrame = 72
+ endFrame = 72,
)
}
@@ -44,11 +46,9 @@ class MainActivity : PreviewActivity() {
motionVideoPlayer.seekBar.progress = newProgress
video.motionComposerView.forFrame(newProgress)
},
- sensitivity = 5f
+ sensitivity = 5f,
)
}
- override fun getMotionVideo(): MotionVideoProducer {
- return video
- }
-}
\ No newline at end of file
+ override fun getMotionVideo(): MotionVideoProducer = video
+}
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/ivi_demo/extension/ViewExtensions.kt
index d3f27159..5b88c362 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/extension/ViewExtensions.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/extension/ViewExtensions.kt
@@ -11,7 +11,7 @@ fun View.enableSwipeSeek(
maxProgress: Int,
initialProgress: () -> Int,
onProgressChanged: (Int) -> Unit,
- sensitivity: Float = 2f
+ sensitivity: Float = 2f,
) {
var lastX = 0f
var progressOnStart = 0
@@ -32,7 +32,9 @@ fun View.enableSwipeSeek(
true
}
- else -> false
+ else -> {
+ false
+ }
}
}
}
@@ -42,7 +44,7 @@ fun View.enableSwipeSeekReverse(
maxProgress: Int,
initialProgress: () -> Int,
onProgressChanged: (Int) -> Unit,
- sensitivity: Float = 2f
+ sensitivity: Float = 2f,
) {
var lastX = 0f
var progressOnStart = 0
@@ -63,7 +65,9 @@ fun View.enableSwipeSeekReverse(
true
}
- else -> false
+ else -> {
+ false
+ }
}
}
}
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/ivi_demo/motion/RenaultCar.kt
index 06a1b2b6..69acfae8 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/RenaultCar.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/RenaultCar.kt
@@ -14,47 +14,61 @@ import java.io.InputStream
import java.util.Locale
import kotlin.math.min
-class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
- BaseContourMotionView(context, startFrame, endFrame) {
-
+class RenaultCar(
+ context: Context,
+ startFrame: Int,
+ endFrame: Int,
+) : BaseContourMotionView(context, startFrame, endFrame) {
companion object {
private const val TAG = "RenaultCar"
const val imageAssetSubFolder = "renault_kiger_bg"
const val roadAssetSubFolder = "road"
}
- private val imageViewBg: ImageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.FIT_XY
- }
+ private val imageViewBg: ImageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.FIT_XY
+ }
- private val imageView: ImageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
+ private val imageView: ImageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
private val assetManager = context.assets
private val files = assetManager.list(imageAssetSubFolder)
private val roadFiles = assetManager.list(roadAssetSubFolder)
init {
- imageViewBg.layoutBy(x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- }, y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- })
-
- imageView.layoutBy(x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- }, y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- })
+ imageViewBg.layoutBy(
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
+ )
+
+ imageView.layoutBy(
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
+ )
contourHeightOf {
MotionConfig.aspectRatio.height.toYInt()
@@ -67,23 +81,25 @@ class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val backgroundColor: Int = MotionInterpolator.interpolateColorForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt())
- )
+ val backgroundColor: Int =
+ MotionInterpolator.interpolateColorForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt()),
+ )
setBackgroundColor(
- backgroundColor
+ backgroundColor,
)
- val scaleInterpolator = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.BACK_IN_OUT),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(0.5f, 1.0f)
- )
+ val scaleInterpolator =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.BACK_IN_OUT),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(0.5f, 1.0f),
+ )
imageView.scaleX = scaleInterpolator
imageView.scaleY = scaleInterpolator
@@ -112,12 +128,13 @@ class RenaultCar(context: Context, startFrame: Int, endFrame: Int) :
// Log.e(TAG, "Error loading image from asset: $road", e)
// }
- val imageName = String.format(
- Locale.getDefault(),
- "%s/%d.png",
- imageAssetSubFolder,
- min(frame, (files?.size ?: 1) - 1)
- )
+ val imageName =
+ String.format(
+ Locale.getDefault(),
+ "%s/%d.png",
+ imageAssetSubFolder,
+ min(frame, (files?.size ?: 1) - 1),
+ )
try {
val inputStream: InputStream = assetManager.open(imageName)
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/ivi_demo/motion/Road.kt
index f261cebf..e08df4e3 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/Road.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/motion/Road.kt
@@ -10,30 +10,38 @@ import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
import java.io.IOException
import java.io.InputStream
-class Road(context: Context, startFrame: Int, endFrame: Int) :
- BaseContourMotionView(context, startFrame, endFrame) {
-
+class Road(
+ context: Context,
+ startFrame: Int,
+ endFrame: Int,
+) : BaseContourMotionView(context, startFrame, endFrame) {
companion object {
private const val TAG = "Road"
const val imageAssetSubFolder = "road"
}
- private val imageView: ImageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
+ private val imageView: ImageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
private val assetManager = context.assets
init {
- imageView.layoutBy(x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- }, y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- })
+ imageView.layoutBy(
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
+ )
contourHeightOf {
MotionConfig.aspectRatio.height.toYInt()
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/ivi_demo/sequence/RenaultSequence.kt
index 93baa0dc..18b04e12 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/sequence/RenaultSequence.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/sequence/RenaultSequence.kt
@@ -8,28 +8,33 @@ import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
import com.tejpratapsingh.motionlib.core.motion.MotionVideoProducer
fun sampleMotionVideo(applicationContext: Context): MotionVideoProducer {
- val motionConfig = MotionConfig(
- aspectRatio = VideoAspectRatio.Ratio9x16_480, fps = 30
- )
+ val motionConfig =
+ MotionConfig(
+ aspectRatio = VideoAspectRatio.Ratio9x16_480,
+ fps = 30,
+ )
val assetManager = applicationContext.assets
val files = assetManager.list(RenaultCar.imageAssetSubFolder)
- val motionView: BaseContourMotionView = RenaultCar(
- context = applicationContext,
- startFrame = 1,
- endFrame = files?.size ?: 1
- )
+ val motionView: BaseContourMotionView =
+ RenaultCar(
+ context = applicationContext,
+ startFrame = 1,
+ endFrame = files?.size ?: 1,
+ )
- val motionView2: BaseContourMotionView = RenaultCar(
- context = applicationContext,
- startFrame = 1,
- endFrame = 55000
- )
+ val motionView2: BaseContourMotionView =
+ RenaultCar(
+ context = applicationContext,
+ startFrame = 1,
+ endFrame = 55000,
+ )
- return MotionVideoProducer.with(
- context = applicationContext,
- config = motionConfig,
- ).addMotionViewToSequence(motionView = motionView)
+ return MotionVideoProducer
+ .with(
+ context = applicationContext,
+ config = motionConfig,
+ ).addMotionViewToSequence(motionView = motionView)
.addMotionViewToSequence(motionView = motionView2)
-}
\ No newline at end of file
+}
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/ivi_demo/view/TrapezoidImageView.kt
index ca84f1f6..7298f227 100644
--- a/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/view/TrapezoidImageView.kt
+++ b/modules/ivi-demo/src/main/java/com/tejpratapsingh/ivi_demo/view/TrapezoidImageView.kt
@@ -9,45 +9,56 @@ import android.util.AttributeSet
import android.view.View
import androidx.core.graphics.withMatrix
-class TrapezoidImageView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null
-) : View(context, attrs) {
+class TrapezoidImageView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ ) : View(context, attrs) {
+ private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private var bitmap: Bitmap? = null
- private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
- private var bitmap: Bitmap? = null
-
- fun setImageBitmap(bmp: Bitmap) {
- bitmap = bmp
- invalidate()
- }
+ fun setImageBitmap(bmp: Bitmap) {
+ bitmap = bmp
+ invalidate()
+ }
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- val bmp = bitmap ?: return
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ val bmp = bitmap ?: return
- val w = bmp.width.toFloat()
- val h = bmp.height.toFloat()
+ val w = bmp.width.toFloat()
+ val h = bmp.height.toFloat()
- // Destination trapezoid (isosceles)
- val topInset = w * 0.3f // how much shorter the top is
- val src = floatArrayOf(
- 0f, 0f, // top-left
- w, 0f, // top-right
- 0f, h, // bottom-left
- w, h // bottom-right
- )
- val dst = floatArrayOf(
- topInset, 0f, // top-left
- w - topInset, 0f, // top-right
- 0f, h, // bottom-left
- w, h // bottom-right
- )
+ // Destination trapezoid (isosceles)
+ val topInset = w * 0.3f // how much shorter the top is
+ val src =
+ floatArrayOf(
+ 0f,
+ 0f, // top-left
+ w,
+ 0f, // top-right
+ 0f,
+ h, // bottom-left
+ w,
+ h, // bottom-right
+ )
+ val dst =
+ floatArrayOf(
+ topInset,
+ 0f, // top-left
+ w - topInset,
+ 0f, // top-right
+ 0f,
+ h, // bottom-left
+ w,
+ h, // bottom-right
+ )
- val matrix = Matrix()
- matrix.setPolyToPoly(src, 0, dst, 0, 4)
- canvas.withMatrix(matrix) {
- drawBitmap(bmp, 0f, 0f, paint)
+ val matrix = Matrix()
+ matrix.setPolyToPoly(src, 0, dst, 0, 4)
+ canvas.withMatrix(matrix) {
+ drawBitmap(bmp, 0f, 0f, paint)
+ }
}
}
-}
diff --git a/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt b/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt
index 3b559ec3..a3032286 100644
--- a/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt
+++ b/modules/ivi-demo/src/test/java/com/tejpratapsingh/ivi_demo/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/FileExtension.kt b/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/FileExtension.kt
index 721416d0..41dc33dc 100644
--- a/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/FileExtension.kt
+++ b/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/FileExtension.kt
@@ -16,9 +16,7 @@ fun File.getVideoMetadata(): DemuxerTrackMeta {
return vt.meta
}
-fun File.getSingleFrameFromVideo(frameNumber: Int): Bitmap {
- return AndroidFrameGrab.getFrame(this, frameNumber)
-}
+fun File.getSingleFrameFromVideo(frameNumber: Int): Bitmap = AndroidFrameGrab.getFrame(this, frameNumber)
/**
* Should not use this, it will require a lot of memory
@@ -31,4 +29,4 @@ fun File.getAllFramesFromFile(): MutableList {
bitmapList.add(AndroidUtil.toBitmap(picture))
}
return bitmapList
-}
\ No newline at end of file
+}
diff --git a/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/JCodecVideoProducerAdapter.kt b/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/JCodecVideoProducerAdapter.kt
index 30a64eb5..5e27b69a 100644
--- a/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/JCodecVideoProducerAdapter.kt
+++ b/modules/jcodec-motion-ext/src/main/java/com/tejpratapsingh/motionlib/jcodec/JCodecVideoProducerAdapter.kt
@@ -12,7 +12,6 @@ import org.jcodec.api.android.AndroidSequenceEncoder
import java.io.File
class JCodecVideoProducerAdapter : VideoProducerAdapter {
-
companion object {
private const val TAG = "JCodecVideoProducerAdap"
}
@@ -24,7 +23,7 @@ class JCodecVideoProducerAdapter : VideoProducerAdapter {
motionAudio: List,
totalFrames: Int,
outputFile: File,
- progressListener: ((Int, Bitmap) -> Unit)?
+ progressListener: ((Int, Bitmap) -> Unit)?,
): File {
if (outputFile.exists()) {
outputFile.delete()
@@ -34,7 +33,9 @@ class JCodecVideoProducerAdapter : VideoProducerAdapter {
for (i in 1..totalFrames) {
Log.d(TAG, "produceVideo: frame $i")
val frameBitmap: Bitmap =
- motionComposerView.forFrame(i).getViewBitmap()
+ motionComposerView
+ .forFrame(i)
+ .getViewBitmap()
.compressToBitmap(motionConfig.outputQuality)
encoder.encodeImage(frameBitmap)
@@ -51,4 +52,4 @@ class JCodecVideoProducerAdapter : VideoProducerAdapter {
return outputFile
}
-}
\ No newline at end of file
+}
diff --git a/modules/jcodec-motion-ext/src/test/java/com/tejpratapsingh/motionlib/jcodec/ExampleUnitTest.kt b/modules/jcodec-motion-ext/src/test/java/com/tejpratapsingh/motionlib/jcodec/ExampleUnitTest.kt
index 94e64e0b..1dee2512 100644
--- a/modules/jcodec-motion-ext/src/test/java/com/tejpratapsingh/motionlib/jcodec/ExampleUnitTest.kt
+++ b/modules/jcodec-motion-ext/src/test/java/com/tejpratapsingh/motionlib/jcodec/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/ExampleInstrumentedTest.kt b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/ExampleInstrumentedTest.kt
index 31ac6088..5577ea08 100644
--- a/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/ExampleInstrumentedTest.kt
+++ b/modules/lyrics-maker/src/androidTest/java/com/tejpratapsingh/lyricsmaker/ExampleInstrumentedTest.kt
@@ -19,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tejpratapsingh.lyricsmaker", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/AlbumArtFetcher.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/AlbumArtFetcher.kt
index 10ed83cf..98ab32cd 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/AlbumArtFetcher.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/AlbumArtFetcher.kt
@@ -16,61 +16,68 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
object AlbumArtFetcher {
- private val client = HttpClient(CIO) {
- install(ContentNegotiation) {
- json(Json {
- ignoreUnknownKeys = true
- isLenient = true
- })
- }
- defaultRequest {
- url {
- protocol = URLProtocol.HTTPS
- host = "musicbrainz.org"
+ private val client =
+ HttpClient(CIO) {
+ install(ContentNegotiation) {
+ json(
+ Json {
+ ignoreUnknownKeys = true
+ isLenient = true
+ },
+ )
+ }
+ defaultRequest {
+ url {
+ protocol = URLProtocol.HTTPS
+ host = "musicbrainz.org"
+ }
+ headers.append(
+ HttpHeaders.UserAgent,
+ "AlbumArtFetcher/1.0 (lyrics@tejpratapsingh.com)",
+ )
}
- headers.append(
- HttpHeaders.UserAgent,
- "AlbumArtFetcher/1.0 (lyrics@tejpratapsingh.com)"
- )
}
- }
- enum class CoverSize(val suffix: String) {
- ORIGINAL(""), // full resolution
+ enum class CoverSize(
+ val suffix: String,
+ ) {
+ ORIGINAL(""), // full resolution
SMALL("-250"),
MEDIUM("-500"),
- LARGE("-1200")
+ LARGE("-1200"),
}
suspend fun fetchAlbumArtUrl(
trackName: String,
artistName: String,
- size: CoverSize = CoverSize.SMALL
+ size: CoverSize = CoverSize.SMALL,
): String? {
- val response: MusicBrainzResponse = client.get("/ws/2/recording") {
- parameter("query", "recording:\"$trackName\" AND artist:\"$artistName\"")
- parameter("fmt", "json")
- }.body()
+ val response: MusicBrainzResponse =
+ client
+ .get("/ws/2/recording") {
+ parameter("query", "recording:\"$trackName\" AND artist:\"$artistName\"")
+ parameter("fmt", "json")
+ }.body()
- val releaseId = response.recordings
- .firstOrNull()
- ?.releases
- ?.firstOrNull()
- ?.id ?: return null
+ val releaseId =
+ response.recordings
+ .firstOrNull()
+ ?.releases
+ ?.firstOrNull()
+ ?.id ?: return null
// Add size suffix
return "https://coverartarchive.org/release/$releaseId/front${size.suffix}"
}
- suspend fun fetchAlbumArtBitmap(url: String): Bitmap? {
- return try {
+ suspend fun fetchAlbumArtBitmap(url: String): Bitmap? =
+ try {
val bytes: ByteArray = client.get(url).body()
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
} catch (e: Exception) {
e.printStackTrace()
null
}
- }
fun close() {
client.close()
@@ -79,15 +86,15 @@ object AlbumArtFetcher {
@Serializable
data class MusicBrainzResponse(
- val recordings: List = emptyList()
+ val recordings: List = emptyList(),
)
@Serializable
data class Recording(
- val releases: List = emptyList()
+ val releases: List = emptyList(),
)
@Serializable
data class Release(
- val id: String
+ val id: String,
)
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/LrcLibClient.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/LrcLibClient.kt
index 852d9265..f7a2ad27 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/LrcLibClient.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/client/LrcLibClient.kt
@@ -13,30 +13,34 @@ import kotlinx.serialization.json.Json
class LrcLibClient(
private val baseUrl: String = "https://lrclib.net/api",
- private val apiKey: String? = null
+ private val apiKey: String? = null,
) {
- private val client = HttpClient(CIO) {
- install(ContentNegotiation) {
- json(Json {
- ignoreUnknownKeys = true
- })
+ private val client =
+ HttpClient(CIO) {
+ install(ContentNegotiation) {
+ json(
+ Json {
+ ignoreUnknownKeys = true
+ },
+ )
+ }
}
- }
- suspend fun searchLyrics(query: SearchQuery): List {
- return client.get("$baseUrl/search") {
- parameter("q", query.searchTerm)
- }.body()
- }
+ suspend fun searchLyrics(query: SearchQuery): List =
+ client
+ .get("$baseUrl/search") {
+ parameter("q", query.searchTerm)
+ }.body()
suspend fun getLyrics(query: LyricsQuery): LyricsResponse? {
- val response = client.get("$baseUrl/get") {
- query.id?.let { parameter("id", it) }
- query.trackName?.let { parameter("track_name", it) }
- query.artistName?.let { parameter("artist_name", it) }
- query.albumName?.let { parameter("album_name", it) }
- query.duration?.let { parameter("duration", it) }
- }
+ val response =
+ client.get("$baseUrl/get") {
+ query.id?.let { parameter("id", it) }
+ query.trackName?.let { parameter("track_name", it) }
+ query.artistName?.let { parameter("artist_name", it) }
+ query.albumName?.let { parameter("album_name", it) }
+ query.duration?.let { parameter("duration", it) }
+ }
return if (response.status.value == 404) null else response.body()
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsQuery.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsQuery.kt
index 860dacc0..1e95f1b9 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsQuery.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsQuery.kt
@@ -8,5 +8,5 @@ data class LyricsQuery(
val trackName: String? = null,
val artistName: String? = null,
val albumName: String? = null,
- val duration: Int? = null
-)
\ No newline at end of file
+ val duration: Int? = null,
+)
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsResponse.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsResponse.kt
index c0963fee..a530dc2a 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsResponse.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/LyricsResponse.kt
@@ -18,13 +18,12 @@ data class LyricsResponse(
val plainLyrics: String? = null,
val syncedLyrics: String? = null,
) : Parcelable {
- fun getLyrics(): String {
- return if (syncedLyrics.isNullOrEmpty()) {
+ fun getLyrics(): String =
+ if (syncedLyrics.isNullOrEmpty()) {
"[0:00.00] No Lyrics Found"
} else {
syncedLyrics
}
- }
fun getReadableDuration(): String {
val totalSeconds = (duration ?: 0f).toInt()
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/SearchQuery.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/SearchQuery.kt
index cadf1170..f4b942c6 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/SearchQuery.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/api/model/SearchQuery.kt
@@ -3,4 +3,6 @@ package com.tejpratapsingh.lyricsmaker.data.api.model
import kotlinx.serialization.Serializable
@Serializable
-data class SearchQuery(val searchTerm: String)
+data class SearchQuery(
+ val searchTerm: String,
+)
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcHelper.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcHelper.kt
index e8a039d8..803cb0b2 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcHelper.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcHelper.kt
@@ -2,10 +2,9 @@ package com.tejpratapsingh.lyricsmaker.data.lrc
object LrcHelper {
fun getSyncedLyrics(
- lrcContent: String, fps: Int
- ): List {
- return getSyncedLyricsWithFrameOffset(lrcContent, fps)
- }
+ lrcContent: String,
+ fps: Int,
+ ): List = getSyncedLyricsWithFrameOffset(lrcContent, fps)
/**
* Parses the raw LRC string into synced lyric frames.
@@ -14,18 +13,22 @@ object LrcHelper {
* @param parser Custom parser if needed
*/
fun getSyncedLyricsWithFrameOffset(
- lrcContent: String, fps: Int, offsetFrames: Int = 0,
+ lrcContent: String,
+ fps: Int,
+ offsetFrames: Int = 0,
parser: LrcParser = LrcParser(),
): List {
val parsedResult = parser.parse(lrcContent)
- return parsedResult.map {
- val frame =
- ((it.time / (1000.0 / fps)).toInt() - offsetFrames).coerceAtLeast(0) // avoid negative frames
- SyncedLyricFrame(
- frame = frame, text = it.text
- )
- }.sortedBy { it.frame }
+ return parsedResult
+ .map {
+ val frame =
+ ((it.time / (1000.0 / fps)).toInt() - offsetFrames).coerceAtLeast(0) // avoid negative frames
+ SyncedLyricFrame(
+ frame = frame,
+ text = it.text,
+ )
+ }.sortedBy { it.frame }
}
/**
@@ -33,7 +36,9 @@ object LrcHelper {
* Converts ms to frames before shifting.
*/
fun getSyncedLyricsWithMsOffset(
- lrcContent: String, fps: Int, offsetMs: Long = 0L
+ lrcContent: String,
+ fps: Int,
+ offsetMs: Long = 0L,
): List {
val offsetFrames = (offsetMs / (1000.0 / fps)).toInt()
return getSyncedLyricsWithFrameOffset(lrcContent, fps, offsetFrames)
@@ -43,17 +48,15 @@ object LrcHelper {
* Find the current lyric line for a given frame
*/
fun getCurrentLyric(
- lyrics: List, currentFrame: Int
- ): SyncedLyricFrame? {
- return lyrics.lastOrNull { it.frame <= currentFrame }
- }
+ lyrics: List,
+ currentFrame: Int,
+ ): SyncedLyricFrame? = lyrics.lastOrNull { it.frame <= currentFrame }
/**
* Find the next lyric line for a given frame
*/
fun getNextLyric(
- lyrics: List, currentFrame: Int
- ): SyncedLyricFrame? {
- return lyrics.firstOrNull { it.frame > currentFrame }
- }
-}
\ No newline at end of file
+ lyrics: List,
+ currentFrame: Int,
+ ): SyncedLyricFrame? = lyrics.firstOrNull { it.frame > currentFrame }
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcLine.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcLine.kt
index a24107d6..a9731074 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcLine.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcLine.kt
@@ -2,5 +2,5 @@ package com.tejpratapsingh.lyricsmaker.data.lrc
data class LrcLine(
val time: Long, // in ms
- val text: String
-)
\ No newline at end of file
+ val text: String,
+)
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcParser.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcParser.kt
index 4d14f2ad..4e163c9f 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcParser.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/LrcParser.kt
@@ -13,7 +13,11 @@ class LrcParser {
matches.forEach { match ->
val min = match.groupValues[1].toInt()
val sec = match.groupValues[2].toInt()
- val ms = match.groupValues.getOrNull(3)?.padEnd(3, '0')?.toIntOrNull() ?: 0
+ val ms =
+ match.groupValues
+ .getOrNull(3)
+ ?.padEnd(3, '0')
+ ?.toIntOrNull() ?: 0
val timeMs = (min * 60 * 1000 + sec * 1000 + ms).toLong()
lines.add(LrcLine(timeMs, lyricText))
@@ -22,4 +26,4 @@ class LrcParser {
return lines.sortedBy { it.time }
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/SyncedLyricFrame.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/SyncedLyricFrame.kt
index 75eebf5e..4882283f 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/SyncedLyricFrame.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/lrc/SyncedLyricFrame.kt
@@ -8,5 +8,5 @@ import kotlinx.serialization.Serializable
@Parcelize
data class SyncedLyricFrame(
val frame: Int,
- val text: String
+ val text: String,
) : Parcelable
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/store/RecentSearchHelper.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/store/RecentSearchHelper.kt
index b5ae2b6b..9e866aca 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/store/RecentSearchHelper.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/data/store/RecentSearchHelper.kt
@@ -8,13 +8,18 @@ object RecentSearchHelper {
private const val PREF_NAME = "recent_searches"
private const val KEY_SEARCHES = "searches"
- fun saveSearch(context: Context, query: String) {
+ fun saveSearch(
+ context: Context,
+ query: String,
+ ) {
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
val searches = getSearches(context).toMutableList()
searches.remove(query)
searches.add(0, query)
- if (searches.size > 10) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
- searches.removeLast()
+ if (searches.size > 10) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ searches.removeLast()
+ }
}
prefs.edit { putStringSet(KEY_SEARCHES, searches.toSet()) }
}
@@ -23,4 +28,4 @@ object RecentSearchHelper {
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
return prefs.getStringSet(KEY_SEARCHES, emptySet())?.toList() ?: emptyList()
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/domain/ListExtensions.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/domain/ListExtensions.kt
index 109a30ee..7f7610c9 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/domain/ListExtensions.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/domain/ListExtensions.kt
@@ -1,5 +1,3 @@
package com.tejpratapsingh.lyricsmaker.domain
-fun List.ensureArrayList(): ArrayList {
- return this as? ArrayList ?: ArrayList(this)
-}
\ No newline at end of file
+fun List.ensureArrayList(): ArrayList = this as? ArrayList ?: ArrayList(this)
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/LyricsActivity.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/LyricsActivity.kt
index a7654372..fc777818 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/LyricsActivity.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/LyricsActivity.kt
@@ -14,9 +14,7 @@ import com.tejpratapsingh.motionlib.activities.PreviewActivity
import com.tejpratapsingh.motionlib.core.MotionConfig
import com.tejpratapsingh.motionlib.core.motion.MotionVideoProducer
-
class LyricsActivity : PreviewActivity() {
-
companion object {
private const val TAG = "LyricsActivity"
@@ -27,14 +25,15 @@ class LyricsActivity : PreviewActivity() {
context: Context,
song: String,
lyrics: ArrayList,
- socialMeta: SocialMeta? = null
+ socialMeta: SocialMeta? = null,
) {
context.startActivity(
Intent(context, LyricsActivity::class.java).also {
it.putExtra(SONG, song)
it.putExtra(ShareReceiverActivity.EXTRA_METADATA, socialMeta)
it.putParcelableArrayListExtra(LYRICS, lyrics)
- })
+ },
+ )
}
}
@@ -42,13 +41,14 @@ class LyricsActivity : PreviewActivity() {
get() = intent.getStringExtra(SONG) ?: ""
private val lyrics: List
- get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- intent.getParcelableArrayListExtra(LYRICS, SyncedLyricFrame::class.java)?.toList()
- ?: emptyList()
- } else {
- @Suppress("DEPRECATION")
- intent.getParcelableArrayListExtra(LYRICS) ?: emptyList()
- }
+ get() =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ intent.getParcelableArrayListExtra(LYRICS, SyncedLyricFrame::class.java)?.toList()
+ ?: emptyList()
+ } else {
+ @Suppress("DEPRECATION")
+ intent.getParcelableArrayListExtra(LYRICS) ?: emptyList()
+ }
private val socialMeta
get() = ShareReceiverActivity.readMetadataFromIntent(intent)
@@ -58,7 +58,7 @@ class LyricsActivity : PreviewActivity() {
applicationContext = applicationContext,
song = song,
lyrics = lyrics,
- image = socialMeta?.image
+ image = socialMeta?.image,
)
}
@@ -68,26 +68,27 @@ class LyricsActivity : PreviewActivity() {
val start = lyrics.minBy { it.frame }.frame
val end = lyrics.maxBy { it.frame }.frame
- MaterialAlertDialogBuilder(this).setTitle("Lyrics").setMessage(
- """
+ MaterialAlertDialogBuilder(this)
+ .setTitle("Lyrics")
+ .setMessage(
+ """
Rendering video for \"$song\" with ${lyrics.size} lines of lyrics.
Start Frame: $start
End Frame: ${getMotionVideo().totalFrames}
Duration: ${(end - start)} frames (${(end - start) / MotionConfig.fps} seconds)
- """.trimIndent()
- ).setPositiveButton("OK") { dialog, _ ->
- LyricsMotionWorker.startWork(
- context = applicationContext,
- song = song,
- lyrics = lyrics,
- image = socialMeta?.image
- )
- }.setNegativeButton("Cancel") { dialog, _ ->
- dialog.dismiss()
- }.setCancelable(false).show()
+ """.trimIndent(),
+ ).setPositiveButton("OK") { dialog, _ ->
+ LyricsMotionWorker.startWork(
+ context = applicationContext,
+ song = song,
+ lyrics = lyrics,
+ image = socialMeta?.image,
+ )
+ }.setNegativeButton("Cancel") { dialog, _ ->
+ dialog.dismiss()
+ }.setCancelable(false)
+ .show()
}
- override fun getMotionVideo(): MotionVideoProducer {
- return video
- }
-}
\ No newline at end of file
+ override fun getMotionVideo(): MotionVideoProducer = video
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/SearchActivity.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/SearchActivity.kt
index 94a95018..7536f3c3 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/SearchActivity.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/activity/SearchActivity.kt
@@ -22,7 +22,6 @@ import com.tejpratapsingh.motion.metadataextractor.ShareReceiverActivity
import kotlinx.coroutines.launch
class SearchActivity : ComponentActivity() {
-
private val lyricsViewModel: LyricsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
@@ -32,11 +31,14 @@ class SearchActivity : ComponentActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(
- this, Manifest.permission.POST_NOTIFICATIONS
+ this,
+ Manifest.permission.POST_NOTIFICATIONS,
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
- this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0
+ this,
+ arrayOf(Manifest.permission.POST_NOTIFICATIONS),
+ 0,
)
}
}
@@ -46,7 +48,7 @@ class SearchActivity : ComponentActivity() {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
AppNavHost(
viewModel = lyricsViewModel,
- modifier = Modifier.padding(innerPadding)
+ modifier = Modifier.padding(innerPadding),
)
}
}
@@ -60,4 +62,4 @@ class SearchActivity : ComponentActivity() {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/AppNavHost.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/AppNavHost.kt
index 8ba7e188..06f79de7 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/AppNavHost.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/AppNavHost.kt
@@ -9,13 +9,19 @@ import com.tejpratapsingh.lyricsmaker.domain.ensureArrayList
import com.tejpratapsingh.lyricsmaker.presentation.activity.LyricsActivity
import com.tejpratapsingh.lyricsmaker.presentation.viewmodel.LyricsViewModel
-sealed class Screen(val route: String) {
+sealed class Screen(
+ val route: String,
+) {
object Home : Screen("home")
+
object Lyrics : Screen("lyrics")
}
@Composable
-fun AppNavHost(viewModel: LyricsViewModel, modifier: Modifier) {
+fun AppNavHost(
+ viewModel: LyricsViewModel,
+ modifier: Modifier,
+) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
@@ -26,7 +32,7 @@ fun AppNavHost(viewModel: LyricsViewModel, modifier: Modifier) {
onLyricsSelected = {
viewModel.selectedLyricResponse = it
navController.navigate(Screen.Lyrics.route)
- }
+ },
)
}
@@ -42,9 +48,9 @@ fun AppNavHost(viewModel: LyricsViewModel, modifier: Modifier) {
context = navController.context,
song = viewModel.selectedSongName,
lyrics = viewModel.selectedLyrics.ensureArrayList(),
- socialMeta = viewModel.socialMeta.value
+ socialMeta = viewModel.socialMeta.value,
)
- }
+ },
)
}
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SearchLyricsCompose.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SearchLyricsCompose.kt
index eab5ab29..c137bb4d 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SearchLyricsCompose.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SearchLyricsCompose.kt
@@ -38,7 +38,7 @@ import kotlinx.coroutines.launch
fun SearchScreen(
modifier: Modifier = Modifier,
viewModel: LyricsViewModel,
- onLyricsSelected: (LyricsResponse) -> Unit = {}
+ onLyricsSelected: (LyricsResponse) -> Unit = {},
) {
val context = LocalContext.current
val query = viewModel.query.collectAsState()
@@ -56,16 +56,18 @@ fun SearchScreen(
val keyboardController = LocalSoftwareKeyboardController.current
Column(
- modifier = modifier
- .fillMaxSize()
- .padding(16.dp)
+ modifier =
+ modifier
+ .fillMaxSize()
+ .padding(16.dp),
) {
Text(
text = "Search Lyrics",
style = MaterialTheme.typography.headlineLarge,
- modifier = Modifier
- .align(CenterHorizontally)
- .padding(16.dp)
+ modifier =
+ Modifier
+ .align(CenterHorizontally)
+ .padding(16.dp),
)
OutlinedTextField(
value = query.value,
@@ -76,25 +78,26 @@ fun SearchScreen(
if (isLoading.value) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
- strokeWidth = 2.dp
+ strokeWidth = 2.dp,
)
}
},
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Search),
- keyboardActions = KeyboardActions(
- onSearch = {
- coroutineScope.launch {
- val searchQuery = query.value.trim()
- if (searchQuery.isNotBlank()) {
- keyboardController?.hide()
- RecentSearchHelper.saveSearch(context, searchQuery)
- recentSearches.value = RecentSearchHelper.getSearches(context)
- viewModel.fetchLyrics()
+ keyboardActions =
+ KeyboardActions(
+ onSearch = {
+ coroutineScope.launch {
+ val searchQuery = query.value.trim()
+ if (searchQuery.isNotBlank()) {
+ keyboardController?.hide()
+ RecentSearchHelper.saveSearch(context, searchQuery)
+ recentSearches.value = RecentSearchHelper.getSearches(context)
+ viewModel.fetchLyrics()
+ }
}
- }
- }
- ),
- modifier = Modifier.fillMaxWidth()
+ },
+ ),
+ modifier = Modifier.fillMaxWidth(),
)
if (lyrics.isEmpty()) {
@@ -103,74 +106,82 @@ fun SearchScreen(
LazyColumn {
items(recentSearches.value.size) { idx ->
Card(
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 4.dp)
- .clickable {
- viewModel.query.tryEmit(recentSearches.value[idx])
- coroutineScope.launch {
- keyboardController?.hide()
- viewModel.fetchLyrics()
- }
- },
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant
- )
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp)
+ .clickable {
+ viewModel.query.tryEmit(recentSearches.value[idx])
+ coroutineScope.launch {
+ keyboardController?.hide()
+ viewModel.fetchLyrics()
+ }
+ },
+ colors =
+ CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceVariant,
+ ),
) {
Text(
text = recentSearches.value[idx],
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp)
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
)
}
}
}
} else {
LazyColumn(
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize(),
) {
items(lyrics.size) { item ->
Card(
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 4.dp)
- .clickable { onLyricsSelected(lyrics[item]) },
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceVariant
- )
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(vertical = 4.dp)
+ .clickable { onLyricsSelected(lyrics[item]) },
+ colors =
+ CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceVariant,
+ ),
) {
Text(
text = "${lyrics[item].trackName} - ${lyrics[item].artistName}",
style = MaterialTheme.typography.labelLarge,
- modifier = Modifier.padding(
- start = 16.dp,
- top = 16.dp,
- end = 16.dp,
- bottom = 2.dp
- )
+ modifier =
+ Modifier.padding(
+ start = 16.dp,
+ top = 16.dp,
+ end = 16.dp,
+ bottom = 2.dp,
+ ),
)
Text(
text = "Duration: ${lyrics[item].getReadableDuration()}",
maxLines = 2,
style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(
- start = 16.dp,
- top = 2.dp,
- end = 16.dp,
- bottom = 2.dp
- )
+ modifier =
+ Modifier.padding(
+ start = 16.dp,
+ top = 2.dp,
+ end = 16.dp,
+ bottom = 2.dp,
+ ),
)
Text(
text = lyrics[item].getLyrics(),
maxLines = 2,
style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(
- start = 16.dp,
- top = 2.dp,
- end = 16.dp,
- bottom = 16.dp
- )
+ modifier =
+ Modifier.padding(
+ start = 16.dp,
+ top = 2.dp,
+ end = 16.dp,
+ bottom = 16.dp,
+ ),
)
Spacer(Modifier.height(4.dp))
}
@@ -178,4 +189,4 @@ fun SearchScreen(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SyncedLyricsSelector.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SyncedLyricsSelector.kt
index 6df683e9..52962a76 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SyncedLyricsSelector.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/compose/SyncedLyricsSelector.kt
@@ -38,9 +38,13 @@ import com.tejpratapsingh.motionlib.core.MotionConfig
import kotlin.math.max
import kotlin.math.min
-data class RangeSelection(val start: Int, val end: Int) {
+data class RangeSelection(
+ val start: Int,
+ val end: Int,
+) {
val minIndex get() = min(start, end)
val maxIndex get() = max(start, end)
+
fun contains(index: Int) = index in minIndex..maxIndex
}
@@ -49,29 +53,29 @@ fun SyncedLyricsSelector(
viewModel: LyricsViewModel,
modifier: Modifier = Modifier,
onSelectionChanged: (List) -> Unit = {},
- onFinalize: (List) -> Unit = {}
+ onFinalize: (List) -> Unit = {},
) {
val listState = rememberLazyListState()
val haptics = LocalHapticFeedback.current
var selection by remember { mutableStateOf(null) }
Column(modifier = modifier.fillMaxSize()) {
-
// Selection summary bar
if (selection != null) {
val selected = viewModel.lyrics.subList(selection!!.minIndex, selection!!.maxIndex + 1)
Surface(tonalElevation = 2.dp) {
Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp),
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
+ horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
"Selected ${selected.size} line(s)",
style = MaterialTheme.typography.bodyMedium,
- fontWeight = FontWeight.SemiBold
+ fontWeight = FontWeight.SemiBold,
)
Row {
TextButton(onClick = { onFinalize(selected) }) {
@@ -95,39 +99,41 @@ fun SyncedLyricsSelector(
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
- contentPadding = PaddingValues(vertical = 8.dp)
+ contentPadding = PaddingValues(vertical = 8.dp),
) {
itemsIndexed(viewModel.lyrics) { index, line ->
val isSelected = selection?.contains(index) == true
Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 12.dp, vertical = 4.dp)
- .clip(MaterialTheme.shapes.medium)
- .background(
- if (isSelected) MaterialTheme.colorScheme.primary.copy(alpha = 0.15f)
- else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
- )
- .combinedClickable(
- onClick = {
- if (selection != null) {
- selection = selection!!.copy(end = index)
- }
- },
- onLongClick = {
- haptics.performHapticFeedback(HapticFeedbackType.LongPress)
- selection = RangeSelection(index, index)
- }
- )
- .padding(12.dp),
- verticalAlignment = Alignment.CenterVertically
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 12.dp, vertical = 4.dp)
+ .clip(MaterialTheme.shapes.medium)
+ .background(
+ if (isSelected) {
+ MaterialTheme.colorScheme.primary.copy(alpha = 0.15f)
+ } else {
+ MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
+ },
+ ).combinedClickable(
+ onClick = {
+ if (selection != null) {
+ selection = selection!!.copy(end = index)
+ }
+ },
+ onLongClick = {
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress)
+ selection = RangeSelection(index, index)
+ },
+ ).padding(12.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
// Frame number
Text(
"[${line.frame}]",
style = MaterialTheme.typography.labelMedium,
- modifier = Modifier.width(64.dp)
+ modifier = Modifier.width(64.dp),
)
Spacer(Modifier.width(8.dp))
// Lyric text
@@ -135,18 +141,18 @@ fun SyncedLyricsSelector(
text = line.text.ifEmpty { "…" },
style = MaterialTheme.typography.bodyLarge,
fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal,
- modifier = Modifier.weight(1f)
+ modifier = Modifier.weight(1f),
)
Spacer(Modifier.width(8.dp))
// Time in seconds
Text(
"[${line.frame / MotionConfig.fps} sec]",
style = MaterialTheme.typography.labelMedium,
- modifier = Modifier.width(64.dp)
+ modifier = Modifier.width(64.dp),
)
}
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/LyricsVideoProducer.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/LyricsVideoProducer.kt
index 97100e45..d84a8654 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/LyricsVideoProducer.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/LyricsVideoProducer.kt
@@ -14,27 +14,30 @@ fun getLyricsVideoProducer(
applicationContext: Context,
song: String,
lyrics: List,
- image: String? = null
+ image: String? = null,
): MotionVideoProducer {
-
Log.d("MotionVideoProducer", "getLyricsVideoProducer: ${lyrics.size}")
- val motionConfig = MotionConfig(
- aspectRatio = VideoAspectRatio.Ratio9x16_480, fps = 24
- )
+ val motionConfig =
+ MotionConfig(
+ aspectRatio = VideoAspectRatio.Ratio9x16_480,
+ fps = 24,
+ )
- val motionView = LyricsContainer(
- context = applicationContext,
- startFrame = lyrics.first().frame,
- endFrame = lyrics.last().frame,
- songName = song,
- lyrics = lyrics,
- image = image
- )
+ val motionView =
+ LyricsContainer(
+ context = applicationContext,
+ startFrame = lyrics.first().frame,
+ endFrame = lyrics.last().frame,
+ songName = song,
+ lyrics = lyrics,
+ image = image,
+ )
- return MotionVideoProducer.with(
- context = applicationContext,
- config = motionConfig,
- videoProducerAdapter = FfmpegVideoProducerAdapter()
- ).addMotionViewToSequence(motionView = motionView)
-}
\ No newline at end of file
+ return MotionVideoProducer
+ .with(
+ context = applicationContext,
+ config = motionConfig,
+ videoProducerAdapter = FfmpegVideoProducerAdapter(),
+ ).addMotionViewToSequence(motionView = motionView)
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/MultiLyricsVideoProducer.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/MultiLyricsVideoProducer.kt
index af59da29..13345f50 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/MultiLyricsVideoProducer.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/motion/MultiLyricsVideoProducer.kt
@@ -11,20 +11,24 @@ import com.tejpratapsingh.motionlib.ffmpeg.FfmpegVideoProducerAdapter
import com.tejpratapsingh.motionlib.ui.custom.text.WordBlinkTextView
fun getMultiLyricsVideoProducer(
- applicationContext: Context, song: String, lyrics: List
+ applicationContext: Context,
+ song: String,
+ lyrics: List,
): MotionVideoProducer {
-
Log.d("MotionVideoProducer", "getMultiLyricsVideoProducer: ${lyrics.size}")
- val motionConfig = MotionConfig(
- aspectRatio = VideoAspectRatio.Ratio9x16_480, fps = 24
- )
+ val motionConfig =
+ MotionConfig(
+ aspectRatio = VideoAspectRatio.Ratio9x16_480,
+ fps = 24,
+ )
- val producer = MotionVideoProducer.with(
- context = applicationContext,
- config = motionConfig,
- videoProducerAdapter = FfmpegVideoProducerAdapter()
- )
+ val producer =
+ MotionVideoProducer.with(
+ context = applicationContext,
+ config = motionConfig,
+ videoProducerAdapter = FfmpegVideoProducerAdapter(),
+ )
lyrics.zipWithNext().forEach { (current, next) ->
producer.addMotionViewToSequence(
@@ -33,15 +37,16 @@ fun getMultiLyricsVideoProducer(
text = current.text,
startFrame = current.frame,
endFrame = next.frame,
- textView = AppCompatTextView(applicationContext).apply {
- textSize = 24f
- setTextColor(android.graphics.Color.WHITE)
- setPadding(16, 16, 16, 16)
- textAlignment = AppCompatTextView.TEXT_ALIGNMENT_CENTER
- }
- )
+ textView =
+ AppCompatTextView(applicationContext).apply {
+ textSize = 24f
+ setTextColor(android.graphics.Color.WHITE)
+ setPadding(16, 16, 16, 16)
+ textAlignment = AppCompatTextView.TEXT_ALIGNMENT_CENTER
+ },
+ ),
)
}
return producer
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationChannelType.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationChannelType.kt
index 678a30e3..ab683e46 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationChannelType.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationChannelType.kt
@@ -9,18 +9,18 @@ enum class NotificationChannelType(
val channelId: String,
val channelNameResId: Int, // Changed to Int for resource ID
val channelDescriptionResId: Int, // Changed to Int for resource ID
- val importance: Int
+ val importance: Int,
) {
RENDERING_PROGRESS(
"render_progress_channel",
R.string.notification_channel_rendering_progress_name,
R.string.notification_channel_rendering_progress_description,
- NotificationManager.IMPORTANCE_LOW
+ NotificationManager.IMPORTANCE_LOW,
),
RENDERING_COMPLETED(
"render_completed_channel",
R.string.notification_channel_rendering_completed_name,
R.string.notification_channel_rendering_completed_description,
- NotificationManager.IMPORTANCE_DEFAULT
- );
+ NotificationManager.IMPORTANCE_DEFAULT,
+ ),
}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationFactory.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationFactory.kt
index cd077f86..185d26a8 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationFactory.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/notification/NotificationFactory.kt
@@ -19,30 +19,30 @@ class NotificationFactory {
NotificationChannelType.entries.forEach { channelType ->
// Check if channel already exists to avoid re-creating unnecessarily
if (notificationManager.getNotificationChannel(channelType.channelId) == null) {
- val channel = NotificationChannel(
- channelType.channelId,
- context.getString(channelType.channelNameResId), // Use string resource
- channelType.importance
- ).apply {
- description =
- context.getString(channelType.channelDescriptionResId) // Use string resource
- }
+ val channel =
+ NotificationChannel(
+ channelType.channelId,
+ context.getString(channelType.channelNameResId), // Use string resource
+ channelType.importance,
+ ).apply {
+ description =
+ context.getString(channelType.channelDescriptionResId) // Use string resource
+ }
notificationManager.createNotificationChannel(channel)
}
}
}
}
- fun getRenderProgressNotification(
- context: Context
- ): NotificationCompat.Builder {
+ fun getRenderProgressNotification(context: Context): NotificationCompat.Builder {
val channelType = NotificationChannelType.RENDERING_PROGRESS
// Ensure channel is created.
// If you move channel creation to Application class, this explicit call might not be needed here.
createNotificationChannels(context) // Or a more optimized way to ensure channels are created
- return NotificationCompat.Builder(context, channelType.channelId)
+ return NotificationCompat
+ .Builder(context, channelType.channelId)
.setSmallIcon(R.drawable.ic_notification_burst)
.setContentTitle(context.getString(R.string.notification_render_progress_title)) // Use string resource
.setContentText(context.getString(R.string.notification_render_progress_text_starting)) // Use string resource
@@ -51,15 +51,14 @@ class NotificationFactory {
.setPriority(NotificationCompat.PRIORITY_LOW)
}
- fun getRenderCompleteNotification(
- context: Context
- ): NotificationCompat.Builder {
+ fun getRenderCompleteNotification(context: Context): NotificationCompat.Builder {
val channelType = NotificationChannelType.RENDERING_COMPLETED
// Ensure channel is created
createNotificationChannels(context) // Or a more optimized way to ensure channels are created
- return NotificationCompat.Builder(context, channelType.channelId)
+ return NotificationCompat
+ .Builder(context, channelType.channelId)
.setSmallIcon(R.drawable.ic_notification_burst)
.setContentTitle(context.getString(R.string.notification_render_complete_title)) // Use string resource
.setContentText(context.getString(R.string.notification_render_complete_text)) // Use string resource
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Color.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Color.kt
index b67d36bb..80d03734 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Color.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Color.kt
@@ -8,4 +8,4 @@ val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
-val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
+val Pink40 = Color(0xFF7D5260)
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 493dc638..6aa53c61 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
@@ -10,17 +10,18 @@ import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
-private val DarkColorScheme = darkColorScheme(
- primary = Purple80,
- secondary = PurpleGrey80,
- tertiary = Pink80
-)
-
-private val LightColorScheme = lightColorScheme(
- primary = Purple40,
- secondary = PurpleGrey40,
- tertiary = Pink40
+private val DarkColorScheme =
+ darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80,
+ )
+private val LightColorScheme =
+ lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40,
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
@@ -29,29 +30,35 @@ private val LightColorScheme = lightColorScheme(
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
- */
-)
+ */
+ )
@Composable
fun AnimatorTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
- content: @Composable () -> Unit
+ content: @Composable () -> Unit,
) {
- val colorScheme = when {
- dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
- val context = LocalContext.current
- if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
- }
+ val colorScheme =
+ when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> {
+ DarkColorScheme
+ }
- darkTheme -> DarkColorScheme
- else -> LightColorScheme
- }
+ else -> {
+ LightColorScheme
+ }
+ }
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
- content = content
+ content = content,
)
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Type.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Type.kt
index c3d5c14e..1eeaec0b 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Type.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/ui/theme/Type.kt
@@ -7,14 +7,16 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
-val Typography = Typography(
- bodyLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.5.sp
- )
+val Typography =
+ Typography(
+ bodyLarge =
+ TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp,
+ ),
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
@@ -30,5 +32,5 @@ val Typography = Typography(
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
- */
-)
\ No newline at end of file
+ */
+ )
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeAudioChartView.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeAudioChartView.kt
index f8ce089f..d684f364 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeAudioChartView.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeAudioChartView.kt
@@ -9,76 +9,85 @@ import android.view.View
import kotlin.math.sin
import kotlin.random.Random
-class FakeAudioChartView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : View(context, attrs, defStyle) {
-
- private val barPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.WHITE
- style = Paint.Style.FILL
- }
- private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.WHITE
- alpha = 50
- strokeWidth = 2f
- }
-
- var bars: Int = 48
- var barWidthPx: Float = 20f
- var barSpacingPx: Float = 10f
- var seed: Long = 12345L
-
- /** Control how fast bars move (smaller = slower, larger = faster) */
- var speedFactor: Float = 0.05f // try 0.01f for very slow, 0.1f for fast
-
- private val rngs by lazy {
- List(bars) { Random(seed + it * 7919L) }
- }
-
- /** Current frame externally controlled */
- private var frame: Int = 0
-
- fun setFrame(frameNumber: Int) {
- frame = frameNumber
- invalidate()
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
-
- val w = width.toFloat()
- val h = height.toFloat()
- val totalWidth = bars * barWidthPx + (bars - 1) * barSpacingPx
- val startX = (w - totalWidth) / 2f
-
- for (i in 0 until bars) {
- val r = rngs[i]
-
- // Make each bar evolve slower using speedFactor
- val phase = (frame * speedFactor + i * 0.3f) % 1f
-
- // Use seeded noise with smooth oscillation instead of raw random
- val base = 0.5f + 0.5f * sin((frame * speedFactor + i) * 2.0)
- val noise = r.nextFloat() * 0.2f
- val finalAmp = ((base + noise) * 0.9f).coerceIn(0.0, 1.0)
+class FakeAudioChartView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0,
+ ) : View(context, attrs, defStyle) {
+ private val barPaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = Color.WHITE
+ style = Paint.Style.FILL
+ }
+ private val linePaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = Color.WHITE
+ alpha = 50
+ strokeWidth = 2f
+ }
+
+ var bars: Int = 48
+ var barWidthPx: Float = 20f
+ var barSpacingPx: Float = 10f
+ var seed: Long = 12345L
+
+ /** Control how fast bars move (smaller = slower, larger = faster) */
+ var speedFactor: Float = 0.05f // try 0.01f for very slow, 0.1f for fast
+
+ private val rngs by lazy {
+ List(bars) { Random(seed + it * 7919L) }
+ }
- val barHeight = (finalAmp * h).coerceAtLeast(4.0)
- val x = startX + i * (barWidthPx + barSpacingPx)
- val yTop: Float = (h - barHeight).toFloat() / 2f
- val yBottom: Float = yTop + barHeight.toFloat()
+ /** Current frame externally controlled */
+ private var frame: Int = 0
- canvas.drawRoundRect(
- x, yTop, x + barWidthPx, yBottom,
- barWidthPx / 2f, barWidthPx / 2f, barPaint
- )
- canvas.drawLine(
- x + barWidthPx / 2f, yTop,
- x + barWidthPx / 2f, yBottom,
- linePaint
- )
+ fun setFrame(frameNumber: Int) {
+ frame = frameNumber
+ invalidate()
}
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ val w = width.toFloat()
+ val h = height.toFloat()
+ val totalWidth = bars * barWidthPx + (bars - 1) * barSpacingPx
+ val startX = (w - totalWidth) / 2f
+
+ for (i in 0 until bars) {
+ val r = rngs[i]
+
+ // Make each bar evolve slower using speedFactor
+ val phase = (frame * speedFactor + i * 0.3f) % 1f
+
+ // Use seeded noise with smooth oscillation instead of raw random
+ val base = 0.5f + 0.5f * sin((frame * speedFactor + i) * 2.0)
+ val noise = r.nextFloat() * 0.2f
+ val finalAmp = ((base + noise) * 0.9f).coerceIn(0.0, 1.0)
+
+ val barHeight = (finalAmp * h).coerceAtLeast(4.0)
+ val x = startX + i * (barWidthPx + barSpacingPx)
+ val yTop: Float = (h - barHeight).toFloat() / 2f
+ val yBottom: Float = yTop + barHeight.toFloat()
+
+ canvas.drawRoundRect(
+ x,
+ yTop,
+ x + barWidthPx,
+ yBottom,
+ barWidthPx / 2f,
+ barWidthPx / 2f,
+ barPaint,
+ )
+ canvas.drawLine(
+ x + barWidthPx / 2f,
+ yTop,
+ x + barWidthPx / 2f,
+ yBottom,
+ linePaint,
+ )
+ }
+ }
}
-}
\ No newline at end of file
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeSineWaveView.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeSineWaveView.kt
index d8b2db2c..15bd7c06 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeSineWaveView.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/FakeSineWaveView.kt
@@ -8,66 +8,69 @@ import android.util.AttributeSet
import android.view.View
import kotlin.math.sin
-class FakeSineWaveView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : View(context, attrs, defStyle) {
+class FakeSineWaveView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0,
+ ) : View(context, attrs, defStyle) {
+ private val wavePaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = Color.MAGENTA
+ strokeWidth = 6f
+ style = Paint.Style.STROKE
+ }
+ private val textPaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = Color.WHITE
+ textSize = 32f
+ }
- private val wavePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.MAGENTA
- strokeWidth = 6f
- style = Paint.Style.STROKE
- }
- private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.WHITE
- textSize = 32f
- }
+ /** externally controlled frame */
+ private var frame: Int = 0
- /** externally controlled frame */
- private var frame: Int = 0
+ /** control wave properties */
+ var amplitude: Float = 0.3f // relative height of wave
+ var wavelength: Float = 150f // pixels per wave cycle
+ var speedFactor: Float = 0.05f // animation speed
- /** control wave properties */
- var amplitude: Float = 0.3f // relative height of wave
- var wavelength: Float = 150f // pixels per wave cycle
- var speedFactor: Float = 0.05f // animation speed
+ fun setFrame(frameNumber: Int) {
+ frame = frameNumber
+ invalidate()
+ }
- fun setFrame(frameNumber: Int) {
- frame = frameNumber
- invalidate()
- }
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
+ val w = width.toFloat()
+ val h = height.toFloat()
+ val centerY = h / 2f
- val w = width.toFloat()
- val h = height.toFloat()
- val centerY = h / 2f
+ val pathPoints = mutableListOf()
- val pathPoints = mutableListOf()
+ val phaseShift = frame * speedFactor
- val phaseShift = frame * speedFactor
+ // Generate sine wave points
+ var x = 0f
+ while (x <= w) {
+ val angle = (x / wavelength * 2 * Math.PI + phaseShift).toFloat()
+ val y = centerY + sin(angle) * (h * amplitude)
+ pathPoints.add(x)
+ pathPoints.add(y)
+ x += 4f // step size (smaller = smoother curve)
+ }
- // Generate sine wave points
- var x = 0f
- while (x <= w) {
- val angle = (x / wavelength * 2 * Math.PI + phaseShift).toFloat()
- val y = centerY + sin(angle) * (h * amplitude)
- pathPoints.add(x)
- pathPoints.add(y)
- x += 4f // step size (smaller = smoother curve)
- }
+ // Draw line segments between points
+ for (i in 2 until pathPoints.size step 2) {
+ val x1 = pathPoints[i - 2]
+ val y1 = pathPoints[i - 1]
+ val x2 = pathPoints[i]
+ val y2 = pathPoints[i + 1]
+ canvas.drawLine(x1, y1, x2, y2, wavePaint)
+ }
- // Draw line segments between points
- for (i in 2 until pathPoints.size step 2) {
- val x1 = pathPoints[i - 2]
- val y1 = pathPoints[i - 1]
- val x2 = pathPoints[i]
- val y2 = pathPoints[i + 1]
- canvas.drawLine(x1, y1, x2, y2, wavePaint)
+ // Debug overlay
+ canvas.drawText("Frame: $frame", 20f, 40f, textPaint)
}
-
- // Debug overlay
- canvas.drawText("Frame: $frame", 20f, 40f, textPaint)
}
-}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsContainer.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsContainer.kt
index 670acdcb..fd0cbfc9 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsContainer.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsContainer.kt
@@ -31,9 +31,8 @@ class LyricsContainer(
startFrame: Int,
endFrame: Int,
val lyrics: List,
- image: String? = null
+ image: String? = null,
) : BaseFrameMotionView(context) {
-
companion object {
private const val TAG = "LyricsContainer"
}
@@ -81,11 +80,11 @@ class LyricsContainer(
return@runBlocking
} else {
Log.i(TAG, "Fetching from musicbrainz")
- AlbumArtFetcher.fetchAlbumArtUrl(
- songName.split(" - ")[0],
- songName.split(" - ")[1]
- )
- ?.let { url ->
+ AlbumArtFetcher
+ .fetchAlbumArtUrl(
+ songName.split(" - ")[0],
+ songName.split(" - ")[1],
+ )?.let { url ->
Log.i(TAG, "cover art found: $url")
setImageBitmap(AlbumArtFetcher.fetchAlbumArtBitmap(url))
AlbumArtFetcher.close()
@@ -98,22 +97,24 @@ class LyricsContainer(
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val backgroundColor: Int = MotionInterpolator.interpolateColorForRange(
- Interpolators(Easings.LINEAR),
- frame,
- Pair(startFrame, endFrame),
- Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt())
- )
+ val backgroundColor: Int =
+ MotionInterpolator.interpolateColorForRange(
+ Interpolators(Easings.LINEAR),
+ frame,
+ Pair(startFrame, endFrame),
+ Pair("#2568ff".toColorInt(), "#ba28ff".toColorInt()),
+ )
setBackgroundColor(Color.BLACK)
- MotionInterpolator.getComplementaryColor(
- backgroundColor
- ).also {
- tvSongName.setTextColor(it)
- tvLyricsLine1.setTextColor(it)
- tvLyricsLine2.setTextColor(it)
- }
+ MotionInterpolator
+ .getComplementaryColor(
+ backgroundColor,
+ ).also {
+ tvSongName.setTextColor(it)
+ tvLyricsLine1.setTextColor(it)
+ tvLyricsLine2.setTextColor(it)
+ }
fakeChartView.setFrame(frame)
@@ -128,4 +129,4 @@ class LyricsContainer(
}
override fun getViewBitmap(): Bitmap = this.toBitmap()
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsTextView.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsTextView.kt
index 3b604d74..abba3d84 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsTextView.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/LyricsTextView.kt
@@ -13,16 +13,15 @@ class LyricsTextView(
startFrame: Int,
endFrame: Int,
textView: AppCompatTextView = AppCompatTextView(context),
- fontUrl: String? = null
+ fontUrl: String? = null,
) : AbstractMotionTextView(
- context = context,
- text = lyrics.first().text,
- startFrame = startFrame,
- endFrame = endFrame,
- textView = textView,
- fontUrl = fontUrl
-) {
-
+ context = context,
+ text = lyrics.first().text,
+ startFrame = startFrame,
+ endFrame = endFrame,
+ textView = textView,
+ fontUrl = fontUrl,
+ ) {
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
@@ -31,4 +30,4 @@ class LyricsTextView(
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/SongNameTextView.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/SongNameTextView.kt
index 5dd025a3..48a3ab4f 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/SongNameTextView.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/view/SongNameTextView.kt
@@ -11,9 +11,8 @@ class SongNameTextView(
startFrame: Int,
endFrame: Int,
textView: AppCompatTextView = AppCompatTextView(context),
- fontUrl: String? = null
+ fontUrl: String? = null,
) : AbstractMotionTextView(context, songName, startFrame, endFrame, textView, fontUrl) {
-
init {
textView.text = songName
}
@@ -22,4 +21,4 @@ class SongNameTextView(
super.forFrame(frame)
return this
}
-}
\ No newline at end of file
+}
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 0f25fe0f..0b834360 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
@@ -12,7 +12,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
open class LyricsViewModel : ViewModel() {
-
val socialMeta = MutableStateFlow(null)
val query = MutableStateFlow("")
val isLoading = MutableStateFlow(false)
@@ -23,36 +22,40 @@ open class LyricsViewModel : ViewModel() {
suspend fun fetchLyrics() {
isLoading.value = true
- val results = client.searchLyrics(SearchQuery(query.value)).filter {
- it.syncedLyrics != null
- }
+ val results =
+ client.searchLyrics(SearchQuery(query.value)).filter {
+ it.syncedLyrics != null
+ }
_lyricsList.emit(results)
isLoading.value = false
}
- var selectedLyricResponse: LyricsResponse = LyricsResponse(
- id = 0,
- trackName = "",
- artistName = "",
- )
+ var selectedLyricResponse: LyricsResponse =
+ LyricsResponse(
+ id = 0,
+ trackName = "",
+ artistName = "",
+ )
val selectedSongName: String
get() = "${selectedLyricResponse.trackName} - ${selectedLyricResponse.artistName}"
val lyrics: List
- get() = LrcHelper.getSyncedLyrics(
- lrcContent = selectedLyricResponse.getLyrics(),
- fps = MotionConfig.fps,
- )
+ get() =
+ LrcHelper.getSyncedLyrics(
+ lrcContent = selectedLyricResponse.getLyrics(),
+ fps = MotionConfig.fps,
+ )
var selectedLyrics: List = emptyList()
get() {
val firstFrame = field.first().frame
- return field.map {
- SyncedLyricFrame(
- frame = it.frame - firstFrame,
- text = it.text
- )
- }.sortedBy { it.frame }
+ return field
+ .map {
+ SyncedLyricFrame(
+ frame = it.frame - firstFrame,
+ text = it.text,
+ )
+ }.sortedBy { it.frame }
}
-}
\ No newline at end of file
+}
diff --git a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorker.kt b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorker.kt
index 094f90d3..d76dfefc 100644
--- a/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorker.kt
+++ b/modules/lyrics-maker/src/main/java/com/tejpratapsingh/lyricsmaker/presentation/worker/LyricsMotionWorker.kt
@@ -32,9 +32,10 @@ import java.net.URLConnection
import java.util.Locale
import java.util.UUID
-class LyricsMotionWorker(private val appContext: Context, parameters: WorkerParameters) :
- MotionWorker(appContext, parameters) {
-
+class LyricsMotionWorker(
+ private val appContext: Context,
+ parameters: WorkerParameters,
+) : MotionWorker(appContext, parameters) {
private val notificationManager = NotificationManagerCompat.from(appContext)
private val progressNotificationBuilder: NotificationCompat.Builder by lazy {
@@ -46,49 +47,61 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
}
private fun createForegroundInfo(
- progressNotificationId: Int, notification: Notification
- ): ForegroundInfo {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ progressNotificationId: Int,
+ notification: Notification,
+ ): ForegroundInfo =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
ForegroundInfo(
progressNotificationId,
notification,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,
)
} else {
ForegroundInfo(progressNotificationId, notification)
}
- }
override suspend fun getForegroundInfo(): ForegroundInfo {
// Create the notification for the foreground service
val notification =
- progressNotificationBuilder.setContentTitle("Rendering Video...") // Initial title
+ progressNotificationBuilder
+ .setContentTitle("Rendering Video...") // Initial title
.setProgress(0, 0, true) // Indeterminate progress initially
- .setOngoing(true).build()
+ .setOngoing(true)
+ .build()
return createForegroundInfo(progressNotificationId, notification)
}
- override fun getMotionVideo(inputData: Data): MotionVideoProducer {
- return getLyricsVideoProducer(
+ override fun getMotionVideo(inputData: Data): MotionVideoProducer =
+ getLyricsVideoProducer(
applicationContext = appContext,
song = inputData.getString(SONG) ?: "Unknown Song",
lyrics = Json.decodeFromString(inputData.getString(LYRICS)!!),
image = inputData.getString(IMAGE),
)
- }
- override fun onProgress(totalFrames: Int, currentProgress: Int, bitmap: Bitmap) {
+ override fun onProgress(
+ totalFrames: Int,
+ currentProgress: Int,
+ bitmap: Bitmap,
+ ) {
Log.d(TAG, "onProgress: $currentProgress / $totalFrames")
val percentage = (currentProgress.toDouble() / totalFrames) * 100
- val progressText = String.format(
- Locale.getDefault(), "%d/%d frames completed", currentProgress, totalFrames
- )
+ val progressText =
+ String.format(
+ Locale.getDefault(),
+ "%d/%d frames completed",
+ currentProgress,
+ totalFrames,
+ )
val contentText = String.format(Locale.getDefault(), "%.0f%%", percentage)
val notification =
- progressNotificationBuilder.setProgress(totalFrames, currentProgress, false)
- .setSubText(progressText).setContentText(contentText).build()
+ progressNotificationBuilder
+ .setProgress(totalFrames, currentProgress, false)
+ .setSubText(progressText)
+ .setContentText(contentText)
+ .build()
updateNotification(progressNotificationId, notification)
@@ -107,19 +120,24 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
val intentOpenFile = Intent(Intent.ACTION_VIEW)
val pendingOpenFileIntent = createPendingIntentFor(intentOpenFile, videoFile)
- val completedNotification = completedNotificationBuilder.setContentTitle("Render Complete")
- .setContentText("Video ready: ${videoFile.name}").addAction(
- NotificationCompat.Action(
- R.drawable.ic_menu_share, // Consider using a custom icon
- "Share Video", pendingShareIntent
- )
- ).addAction(
- NotificationCompat.Action(
- R.drawable.ic_media_play, // Consider using a custom icon
- "Open Video", pendingOpenFileIntent
- )
- ).setAutoCancel(true) // Dismiss notification when tapped (if no content intent set)
- .build()
+ val completedNotification =
+ completedNotificationBuilder
+ .setContentTitle("Render Complete")
+ .setContentText("Video ready: ${videoFile.name}")
+ .addAction(
+ NotificationCompat.Action(
+ R.drawable.ic_menu_share, // Consider using a custom icon
+ "Share Video",
+ pendingShareIntent,
+ ),
+ ).addAction(
+ NotificationCompat.Action(
+ R.drawable.ic_media_play, // Consider using a custom icon
+ "Open Video",
+ pendingOpenFileIntent,
+ ),
+ ).setAutoCancel(true) // Dismiss notification when tapped (if no content intent set)
+ .build()
updateNotification(completedNotificationId, completedNotification)
}
@@ -127,7 +145,10 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
@Volatile
private var lastNotificationUpdateTime = 0L
- private fun updateNotification(notificationId: Int, notification: Notification) {
+ private fun updateNotification(
+ notificationId: Int,
+ notification: Notification,
+ ) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastNotificationUpdateTime < 500) {
return
@@ -135,7 +156,8 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
lastNotificationUpdateTime = currentTime
if (ActivityCompat.checkSelfPermission(
- appContext, Manifest.permission.POST_NOTIFICATIONS
+ appContext,
+ Manifest.permission.POST_NOTIFICATIONS,
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(notificationId, notification)
@@ -146,12 +168,19 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
}
}
- private fun createPendingIntentFor(intent: Intent, videoFile: File): PendingIntent {
- val apkURI: Uri = FileProvider.getUriForFile(
- appContext, "${appContext.packageName}.fileprovider", videoFile
- )
+ private fun createPendingIntentFor(
+ intent: Intent,
+ videoFile: File,
+ ): PendingIntent {
+ val apkURI: Uri =
+ FileProvider.getUriForFile(
+ appContext,
+ "${appContext.packageName}.fileprovider",
+ videoFile,
+ )
intent.setDataAndType(
- apkURI, URLConnection.guessContentTypeFromName(videoFile.name)
+ apkURI,
+ URLConnection.guessContentTypeFromName(videoFile.name),
)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(Intent.EXTRA_STREAM, apkURI)
@@ -160,8 +189,10 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
return PendingIntent.getActivity(
- appContext, 0, // requestCode, consider making this unique if you have many such intents
- intent, pendingShareIntentFlags
+ appContext,
+ 0, // requestCode, consider making this unique if you have many such intents
+ intent,
+ pendingShareIntentFlags,
)
}
@@ -176,13 +207,15 @@ class LyricsMotionWorker(private val appContext: Context, parameters: WorkerPara
context: Context,
song: String,
lyrics: List,
- image: String? = null
+ image: String? = null,
): UUID {
- val inputData = Data.Builder()
- .putString(SONG, song)
- .putString(LYRICS, Json.encodeToString(lyrics))
- .putString(IMAGE, image)
- .build()
+ val inputData =
+ Data
+ .Builder()
+ .putString(SONG, song)
+ .putString(LYRICS, Json.encodeToString(lyrics))
+ .putString(IMAGE, image)
+ .build()
val workRequest =
OneTimeWorkRequestBuilder().setInputData(inputData).build()
diff --git a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/ExampleUnitTest.kt b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/ExampleUnitTest.kt
index 38a1b570..64c113d6 100644
--- a/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/ExampleUnitTest.kt
+++ b/modules/lyrics-maker/src/test/java/com/tejpratapsingh/lyricsmaker/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/metadata-extractor/src/androidTest/java/com/tejpratapsingh/motion/metadataextractor/ExampleInstrumentedTest.kt b/modules/metadata-extractor/src/androidTest/java/com/tejpratapsingh/motion/metadataextractor/ExampleInstrumentedTest.kt
index d0f9dc5e..69a35881 100644
--- a/modules/metadata-extractor/src/androidTest/java/com/tejpratapsingh/motion/metadataextractor/ExampleInstrumentedTest.kt
+++ b/modules/metadata-extractor/src/androidTest/java/com/tejpratapsingh/motion/metadataextractor/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package com.tejpratapsingh.motion.metadataextractor
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tejpratapsingh.motion.metadataextractor.test", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/MetadataExtractor.kt b/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/MetadataExtractor.kt
index 03fae00e..b98ebcb6 100644
--- a/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/MetadataExtractor.kt
+++ b/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/MetadataExtractor.kt
@@ -14,7 +14,7 @@ data class SocialMeta(
val image: String? = null,
val siteName: String? = null,
val twitterCard: String? = null,
- val url: String? = null
+ val url: String? = null,
) : Parcelable
suspend fun HttpClient.extractSocialMetadata(url: String): SocialMeta? {
@@ -36,7 +36,7 @@ suspend fun HttpClient.extractSocialMetadata(url: String): SocialMeta? {
image = metaContent("og:image", "twitter:image"),
siteName = metaContent("og:site_name"),
twitterCard = metaContent("twitter:card"),
- url = metaContent("og:url", "twitter:url")
+ url = metaContent("og:url", "twitter:url"),
)
} catch (e: Exception) {
e.printStackTrace()
diff --git a/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/ShareReceiverActivity.kt b/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/ShareReceiverActivity.kt
index 31ee618f..8228ce77 100644
--- a/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/ShareReceiverActivity.kt
+++ b/modules/metadata-extractor/src/main/java/com/tejpratapsingh/motion/metadataextractor/ShareReceiverActivity.kt
@@ -18,20 +18,18 @@ import io.ktor.client.engine.cio.CIO
import kotlinx.coroutines.launch
class ShareReceiverActivity : AppCompatActivity() {
-
companion object {
private const val TAG = "ShareReceiverActivity"
const val EXTRA_METADATA = "extra_metadata"
const val ACTIVITY_INTENT_ACTION = "com.tejpratapsingh.motion.metadataextractor.action.OPEN"
- fun readMetadataFromIntent(intent: Intent): SocialMeta? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ fun readMetadataFromIntent(intent: Intent): SocialMeta? =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(EXTRA_METADATA, SocialMeta::class.java)
} else {
intent.getParcelableExtra(EXTRA_METADATA) as SocialMeta?
}
- }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -47,8 +45,10 @@ class ShareReceiverActivity : AppCompatActivity() {
when (intent?.action) {
Intent.ACTION_SEND -> {
when (intent.type) {
- "text/plain" -> lifecycleScope.launch {
- handleSharedText(intent)
+ "text/plain" -> {
+ lifecycleScope.launch {
+ handleSharedText(intent)
+ }
}
else -> {
@@ -79,13 +79,15 @@ class ShareReceiverActivity : AppCompatActivity() {
findViewById(R.id.btn_next).also { btn ->
btn.setOnClickListener {
- startActivity(Intent(ACTIVITY_INTENT_ACTION).apply {
- putExtra(EXTRA_METADATA, metaData)
- })
+ startActivity(
+ Intent(ACTIVITY_INTENT_ACTION).apply {
+ putExtra(EXTRA_METADATA, metaData)
+ },
+ )
finish()
}
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/metadata-extractor/src/test/java/com/tejpratapsingh/motion/metadataextractor/ExampleUnitTest.kt b/modules/metadata-extractor/src/test/java/com/tejpratapsingh/motion/metadataextractor/ExampleUnitTest.kt
index 498355a7..9b27e140 100644
--- a/modules/metadata-extractor/src/test/java/com/tejpratapsingh/motion/metadataextractor/ExampleUnitTest.kt
+++ b/modules/metadata-extractor/src/test/java/com/tejpratapsingh/motion/metadataextractor/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package com.tejpratapsingh.motion.metadataextractor
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
@@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/activities/PreviewActivity.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/activities/PreviewActivity.kt
index e7a17661..a040e17e 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/activities/PreviewActivity.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/activities/PreviewActivity.kt
@@ -10,7 +10,6 @@ import com.tejpratapsingh.motionlib.core.motion.MotionVideoProducer
import com.tejpratapsingh.motionlib.ui.MotionVideoPlayer
abstract class PreviewActivity : ComponentActivity() {
-
val motionVideoPlayer by lazy { MotionVideoPlayer(applicationContext, getMotionVideo()) }
override fun onCreate(savedInstanceState: Bundle?) {
@@ -22,7 +21,10 @@ abstract class PreviewActivity : ComponentActivity() {
ViewCompat.setOnApplyWindowInsetsListener(motionVideoPlayer) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(
- left = insets.left, top = insets.top, right = insets.right, bottom = insets.bottom
+ left = insets.left,
+ top = insets.top,
+ right = insets.right,
+ bottom = insets.bottom,
)
WindowInsetsCompat.CONSUMED
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/adapter/AndroidVideoProducerAdapter.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/adapter/AndroidVideoProducerAdapter.kt
index 2d6c52a3..e737bd09 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/adapter/AndroidVideoProducerAdapter.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/adapter/AndroidVideoProducerAdapter.kt
@@ -14,7 +14,6 @@ import java.io.File
import java.util.Locale
class AndroidVideoProducerAdapter : VideoProducerAdapter {
-
companion object {
private const val TAG = "AndroidVideoProducerAda"
}
@@ -30,7 +29,7 @@ class AndroidVideoProducerAdapter : VideoProducerAdapter {
motionAudio: List,
totalFrames: Int,
outputFile: File,
- progressListener: ((Int, Bitmap) -> Unit)?
+ progressListener: ((Int, Bitmap) -> Unit)?,
): File {
Log.i(TAG, "produceVideo: starting")
if (outputFile.exists()) {
@@ -46,12 +45,16 @@ class AndroidVideoProducerAdapter : VideoProducerAdapter {
for (i in 1..totalFrames) {
Log.d(TAG, "produceVideo: frame $i")
val frameBitmap: Bitmap =
- motionComposerView.forFrame(i).getViewBitmap()
+ motionComposerView
+ .forFrame(i)
+ .getViewBitmap()
.compressToBitmap(motionConfig.outputQuality)
try {
context.saveBitmapToCacheFolder(
- frameBitmap, subDirName, String.format(Locale.getDefault(), "%05d.png", i)
+ frameBitmap,
+ subDirName,
+ String.format(Locale.getDefault(), "%05d.png", i),
)
} catch (e: Exception) {
Log.e(TAG, "Error saving frame $i: ${e.message}", e)
@@ -68,9 +71,9 @@ class AndroidVideoProducerAdapter : VideoProducerAdapter {
inputDir = subDir,
motionAudio = motionAudio,
outputFile = outputFile,
- motionConfig = motionConfig
+ motionConfig = motionConfig,
)
return outputFile
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Easings.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Easings.kt
index 06979b9f..1181e582 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Easings.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Easings.kt
@@ -32,4 +32,4 @@ enum class Easings {
BOUNCE_IN,
BOUNCE_OUT,
BOUNCE_IN_OUT,
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Interpolators.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Interpolators.kt
index d93dc06d..bd0481e7 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Interpolators.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/Interpolators.kt
@@ -7,39 +7,41 @@ import kotlin.math.asin
import kotlin.math.pow
import kotlin.math.sin
-class Interpolators(val easing: Easings) : Interpolator {
-
- private var interpolator: Interpolator = when (easing) {
- Easings.LINEAR -> PathInterpolatorCompat.create(0f, 0f, 1f, 1f)
- Easings.SIN_IN -> PathInterpolatorCompat.create(.47f, 0f, .745f, .715f)
- Easings.SIN_OUT -> PathInterpolatorCompat.create(.39f, .575f, .565f, 1f)
- Easings.SIN_IN_OUT -> PathInterpolatorCompat.create(.445f, .05f, .55f, .95f)
- Easings.QUAD_IN -> PathInterpolatorCompat.create(.55f, .085f, .68f, .53f)
- Easings.QUAD_OUT -> PathInterpolatorCompat.create(.25f, .46f, .45f, .94f)
- Easings.QUAD_IN_OUT -> PathInterpolatorCompat.create(.455f, .03f, .515f, .955f)
- Easings.CUBIC_IN -> PathInterpolatorCompat.create(.55f, .055f, .675f, .19f)
- Easings.CUBIC_OUT -> PathInterpolatorCompat.create(.215f, .61f, .355f, 1f)
- Easings.CUBIC_IN_OUT -> PathInterpolatorCompat.create(.645f, .045f, .355f, 1f)
- Easings.QUART_IN -> PathInterpolatorCompat.create(.895f, .03f, .685f, .22f)
- Easings.QUART_OUT -> PathInterpolatorCompat.create(.165f, .84f, .44f, 1f)
- Easings.QUART_IN_OUT -> PathInterpolatorCompat.create(.77f, 0f, .175f, 1f)
- Easings.QUINT_IN -> PathInterpolatorCompat.create(.755f, .05f, .855f, .06f)
- Easings.QUINT_OUT -> PathInterpolatorCompat.create(.23f, 1f, .32f, 1f)
- Easings.QUINT_IN_OUT -> PathInterpolatorCompat.create(.86f, 0f, .07f, 1f)
- Easings.EXP_IN -> PathInterpolatorCompat.create(.95f, .05f, .795f, .035f)
- Easings.EXP_OUT -> PathInterpolatorCompat.create(.19f, 1f, .22f, 1f)
- Easings.EXP_IN_OUT -> PathInterpolatorCompat.create(1f, 0f, 0f, 1f)
- Easings.CIRC_IN -> PathInterpolatorCompat.create(.6f, .04f, .98f, .335f)
- Easings.CIRC_OUT -> PathInterpolatorCompat.create(.075f, .82f, .165f, 1f)
- Easings.CIRC_IN_OUT -> PathInterpolatorCompat.create(.785f, .135f, .15f, .86f)
- Easings.BACK_IN -> PathInterpolatorCompat.create(.6f, -.28f, .735f, .045f)
- Easings.BACK_OUT -> PathInterpolatorCompat.create(.175f, .885f, .32f, 1.275f)
- Easings.BACK_IN_OUT -> PathInterpolatorCompat.create(.68f, -.55f, .265f, 1.55f)
- else -> LinearInterpolator()
- }
+class Interpolators(
+ val easing: Easings,
+) : Interpolator {
+ private var interpolator: Interpolator =
+ when (easing) {
+ Easings.LINEAR -> PathInterpolatorCompat.create(0f, 0f, 1f, 1f)
+ Easings.SIN_IN -> PathInterpolatorCompat.create(.47f, 0f, .745f, .715f)
+ Easings.SIN_OUT -> PathInterpolatorCompat.create(.39f, .575f, .565f, 1f)
+ Easings.SIN_IN_OUT -> PathInterpolatorCompat.create(.445f, .05f, .55f, .95f)
+ Easings.QUAD_IN -> PathInterpolatorCompat.create(.55f, .085f, .68f, .53f)
+ Easings.QUAD_OUT -> PathInterpolatorCompat.create(.25f, .46f, .45f, .94f)
+ Easings.QUAD_IN_OUT -> PathInterpolatorCompat.create(.455f, .03f, .515f, .955f)
+ Easings.CUBIC_IN -> PathInterpolatorCompat.create(.55f, .055f, .675f, .19f)
+ Easings.CUBIC_OUT -> PathInterpolatorCompat.create(.215f, .61f, .355f, 1f)
+ Easings.CUBIC_IN_OUT -> PathInterpolatorCompat.create(.645f, .045f, .355f, 1f)
+ Easings.QUART_IN -> PathInterpolatorCompat.create(.895f, .03f, .685f, .22f)
+ Easings.QUART_OUT -> PathInterpolatorCompat.create(.165f, .84f, .44f, 1f)
+ Easings.QUART_IN_OUT -> PathInterpolatorCompat.create(.77f, 0f, .175f, 1f)
+ Easings.QUINT_IN -> PathInterpolatorCompat.create(.755f, .05f, .855f, .06f)
+ Easings.QUINT_OUT -> PathInterpolatorCompat.create(.23f, 1f, .32f, 1f)
+ Easings.QUINT_IN_OUT -> PathInterpolatorCompat.create(.86f, 0f, .07f, 1f)
+ Easings.EXP_IN -> PathInterpolatorCompat.create(.95f, .05f, .795f, .035f)
+ Easings.EXP_OUT -> PathInterpolatorCompat.create(.19f, 1f, .22f, 1f)
+ Easings.EXP_IN_OUT -> PathInterpolatorCompat.create(1f, 0f, 0f, 1f)
+ Easings.CIRC_IN -> PathInterpolatorCompat.create(.6f, .04f, .98f, .335f)
+ Easings.CIRC_OUT -> PathInterpolatorCompat.create(.075f, .82f, .165f, 1f)
+ Easings.CIRC_IN_OUT -> PathInterpolatorCompat.create(.785f, .135f, .15f, .86f)
+ Easings.BACK_IN -> PathInterpolatorCompat.create(.6f, -.28f, .735f, .045f)
+ Easings.BACK_OUT -> PathInterpolatorCompat.create(.175f, .885f, .32f, 1.275f)
+ Easings.BACK_IN_OUT -> PathInterpolatorCompat.create(.68f, -.55f, .265f, 1.55f)
+ else -> LinearInterpolator()
+ }
- override fun getInterpolation(value: Float): Float {
- return when (easing) {
+ override fun getInterpolation(value: Float): Float =
+ when (easing) {
Easings.ELASTIC_IN -> elasticIn(value)
Easings.ELASTIC_OUT -> elasticOut(value)
Easings.ELASTIC_IN_OUT -> elasticInOut(value)
@@ -48,15 +50,15 @@ class Interpolators(val easing: Easings) : Interpolator {
Easings.BOUNCE_IN_OUT -> bounceInOut(value)
else -> interpolator.getInterpolation(value)
}
- }
- private fun bounceIn(t: Float): Float {
- return 1f - bounceOut(1f - t)
- }
+ private fun bounceIn(t: Float): Float = 1f - bounceOut(1f - t)
+
+ private fun bounceOut(t: Float): Float =
+ when {
+ t < 1 / 2.75 -> {
+ (7.5625 * t * t).toFloat()
+ }
- private fun bounceOut(t: Float): Float {
- return when {
- t < 1 / 2.75 -> (7.5625 * t * t).toFloat()
t < 2 / 2.75 -> {
val o = t - 1.5 / 2.75
(7.5625 * o * o + 0.75).toFloat()
@@ -72,7 +74,6 @@ class Interpolators(val easing: Easings) : Interpolator {
(7.5625 * o * o + 0.984375).toFloat()
}
}
- }
private fun bounceInOut(t: Float): Float {
if (t < 0.5) {
@@ -121,4 +122,4 @@ class Interpolators(val easing: Easings) : Interpolator {
(2.0.pow((-10 * o).toDouble()) * sin((o - s) * pi2 / .45) * 0.5 + 1).toFloat()
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/MotionInterpolator.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/MotionInterpolator.kt
index 0be4c61a..cba79aa2 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/MotionInterpolator.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/animation/MotionInterpolator.kt
@@ -30,7 +30,7 @@ object MotionInterpolator {
interpolator: Interpolator,
currentFrame: Int,
frameRange: Pair,
- valueRange: Pair
+ valueRange: Pair,
): Float {
val (startFrame, endFrame) = frameRange
val (startValue, endValue) = valueRange
@@ -57,7 +57,7 @@ object MotionInterpolator {
if (BuildConfig.DEBUG) { // Example: Only log in debug builds
Log.d(
TAG,
- "interpolateForRange: currentFrame: $currentFrame, framePercent: $framePercent, interpolatedFramePercent: $interpolatedFramePercent, valueFromPercent: $valueFromPercent"
+ "interpolateForRange: currentFrame: $currentFrame, framePercent: $framePercent, interpolatedFramePercent: $interpolatedFramePercent, valueFromPercent: $valueFromPercent",
)
}
@@ -76,7 +76,7 @@ object MotionInterpolator {
interpolator: Interpolator,
currentFrame: Int,
frameRange: Pair,
- @ColorInt valueRange: Pair // Added @ColorInt for clarity
+ @ColorInt valueRange: Pair, // Added @ColorInt for clarity
): Int {
val (startFrame, endFrame) = frameRange
val (startColor, endColor) = valueRange
@@ -97,7 +97,7 @@ object MotionInterpolator {
return argbEvaluator.evaluate(
interpolatedFramePercent,
startColor,
- endColor
+ endColor,
) as Int
}
@@ -106,11 +106,15 @@ object MotionInterpolator {
* @param colorToInvert The @ColorInt to invert.
* @return The complementary @ColorInt.
*/
- fun getComplementaryColor(@ColorInt colorToInvert: Int): Int {
+ fun getComplementaryColor(
+ @ColorInt colorToInvert: Int,
+ ): Int {
val hsv = FloatArray(3)
Color.RGBToHSV(
- Color.red(colorToInvert), Color.green(colorToInvert),
- Color.blue(colorToInvert), hsv
+ Color.red(colorToInvert),
+ Color.green(colorToInvert),
+ Color.blue(colorToInvert),
+ hsv,
)
hsv[0] = (hsv[0] + 180) % 360 // Add 180 degrees to hue for complementary color
return Color.HSVToColor(hsv)
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 1fb1bfb3..7719d621 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
@@ -10,15 +10,17 @@ import kotlin.math.sqrt
object Spring {
data class Config(
val stiffness: Double = 100.0, // k
- val damping: Double = 10.0, // c
- val mass: Double = 1.0 // m
+ val damping: Double = 10.0, // c
+ val mass: Double = 1.0, // m
)
- enum class Preset(val cfg: Config) {
+ enum class Preset(
+ val cfg: Config,
+ ) {
GENTLE(Config(stiffness = 120.0, damping = 14.0, mass = 1.0)),
WOBBLY(Config(stiffness = 180.0, damping = 12.0, mass = 1.0)),
STIFF(Config(stiffness = 300.0, damping = 30.0, mass = 1.0)),
- SLOW(Config(stiffness = 70.0, damping = 10.0, mass = 1.0))
+ SLOW(Config(stiffness = 70.0, damping = 10.0, mass = 1.0)),
}
/**
@@ -39,7 +41,7 @@ object Spring {
from: Double = 0.0,
to: Double = 1.0,
config: Config = Config(),
- initialVelocity: Double = 0.0
+ initialVelocity: Double = 0.0,
): Double {
if (from == to) return from
val t = frame.coerceAtLeast(0) / fps
@@ -57,51 +59,52 @@ object Spring {
// normalized initial velocity (units of normalized value per second)
val v0 = initialVelocity / delta
- val xNormalized = when {
- zeta < 1.0 -> {
- // underdamped
- val omegaD = omega0 * sqrt(1.0 - zeta * zeta)
- // avoid division by zero if very small
- if (omegaD.isFinite() && omegaD > 1e-12) {
- val expTerm = exp(-zeta * omega0 * t)
- val cosTerm = cos(omegaD * t)
- val sinTerm = sin(omegaD * t)
- 1.0 - (expTerm / omegaD) * ((v0 + zeta * omega0) * sinTerm + omegaD * cosTerm)
- } else {
- // fallback to simple exponential
- 1.0 - exp(-omega0 * t)
+ val xNormalized =
+ when {
+ zeta < 1.0 -> {
+ // underdamped
+ val omegaD = omega0 * sqrt(1.0 - zeta * zeta)
+ // avoid division by zero if very small
+ if (omegaD.isFinite() && omegaD > 1e-12) {
+ val expTerm = exp(-zeta * omega0 * t)
+ val cosTerm = cos(omegaD * t)
+ val sinTerm = sin(omegaD * t)
+ 1.0 - (expTerm / omegaD) * ((v0 + zeta * omega0) * sinTerm + omegaD * cosTerm)
+ } else {
+ // fallback to simple exponential
+ 1.0 - exp(-omega0 * t)
+ }
}
- }
- zeta == 1.0 -> {
- // critically damped: double root at -omega0
- val expTerm = exp(-omega0 * t)
- // formula for unit-step response with x(0)=0, x'(0)=v0:
- 1.0 - expTerm * (1.0 + (v0 + omega0) * t)
- }
+ zeta == 1.0 -> {
+ // critically damped: double root at -omega0
+ val expTerm = exp(-omega0 * t)
+ // formula for unit-step response with x(0)=0, x'(0)=v0:
+ 1.0 - expTerm * (1.0 + (v0 + omega0) * t)
+ }
- else -> {
- // overdamped
- val sqrtTerm = sqrt(zeta * zeta - 1.0)
- val r1 = -omega0 * (zeta - sqrtTerm)
- val r2 = -omega0 * (zeta + sqrtTerm)
- // solve for coefficients A and B for homogeneous solution that satisfies initial conditions
- // unit-step response => steady-state = 1
- // x(t) = 1 + A*exp(r1*t) + B*exp(r2*t)
- // initial:
- // x(0) = 0 => 1 + A + B = 0 => A + B = -1
- // x'(0) = v0 => A*r1 + B*r2 = v0
- val denom = (r1 - r2)
- if (abs(denom) < 1e-12) {
- // 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)
+ else -> {
+ // overdamped
+ val sqrtTerm = sqrt(zeta * zeta - 1.0)
+ val r1 = -omega0 * (zeta - sqrtTerm)
+ val r2 = -omega0 * (zeta + sqrtTerm)
+ // solve for coefficients A and B for homogeneous solution that satisfies initial conditions
+ // unit-step response => steady-state = 1
+ // x(t) = 1 + A*exp(r1*t) + B*exp(r2*t)
+ // initial:
+ // x(0) = 0 => 1 + A + B = 0 => A + B = -1
+ // x'(0) = v0 => A*r1 + B*r2 = v0
+ val denom = (r1 - r2)
+ if (abs(denom) < 1e-12) {
+ // 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)
+ }
}
}
- }
// map normalized [0..1] back to [from..to]
return from + delta * xNormalized
@@ -121,7 +124,7 @@ object Spring {
config: Config = Config(),
initialVelocity: Double = 0.0,
maxFrames: Int = 300,
- settleThreshold: Double = 1e-3
+ settleThreshold: Double = 1e-3,
): List {
val out = ArrayList(min(maxFrames, 1000))
for (frame in 0 until maxFrames) {
@@ -131,4 +134,4 @@ object Spring {
}
return out
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AndroidVideoGenerator.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AndroidVideoGenerator.kt
index b9cafa89..bf25ebdd 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AndroidVideoGenerator.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AndroidVideoGenerator.kt
@@ -17,7 +17,6 @@ import java.io.IOException
import java.nio.ByteBuffer
class AndroidVideoGenerator {
-
companion object {
private const val TAG = "VideoGenerator"
@@ -65,7 +64,7 @@ class AndroidVideoGenerator {
inputDir: File? = null,
outputFile: File,
motionConfig: MotionConfig,
- motionAudio: List = emptyList()
+ motionAudio: List = emptyList(),
) {
if (bitmaps.isEmpty() && inputDir == null) {
Log.w(TAG, "No bitmaps provided. Cannot generate video.")
@@ -81,11 +80,12 @@ class AndroidVideoGenerator {
val format =
MediaFormat.createVideoFormat(MIME_TYPE, motionConfig.aspectRatio.width, motionConfig.aspectRatio.height)
format.setInteger(
- MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
+ MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
)
format.setInteger(
MediaFormat.KEY_BIT_RATE,
- calculateBitRate(motionConfig.aspectRatio.width, motionConfig.aspectRatio.height, motionConfig.fps)
+ calculateBitRate(motionConfig.aspectRatio.width, motionConfig.aspectRatio.height, motionConfig.fps),
)
format.setInteger(MediaFormat.KEY_FRAME_RATE, motionConfig.fps)
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL)
@@ -137,7 +137,9 @@ class AndroidVideoGenerator {
while (true) {
val encoderStatus = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC)
when {
- encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> break
+ encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> {
+ break
+ }
encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
if (muxerStarted) throw RuntimeException("format changed after muxer start")
@@ -156,14 +158,14 @@ class AndroidVideoGenerator {
encoderStatus < 0 -> {
Log.w(
TAG,
- "unexpected result from encoder.dequeueOutputBuffer: $encoderStatus"
+ "unexpected result from encoder.dequeueOutputBuffer: $encoderStatus",
)
}
else -> {
val encodedData =
mediaCodec.getOutputBuffer(encoderStatus) ?: throw RuntimeException(
- "encoderOutputBuffer $encoderStatus was null"
+ "encoderOutputBuffer $encoderStatus was null",
)
if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
@@ -205,7 +207,7 @@ class AndroidVideoGenerator {
fps = motionConfig.fps,
initialPresentationTimeUs = presentationTimeUs,
audioTrackFormats = audioTrackFormats,
- muxerAudioTrackIndices = muxerAudioTrackIndices
+ muxerAudioTrackIndices = muxerAudioTrackIndices,
)
// Now copy audio samples into the muxer (timeline aligned by frames -> microseconds)
@@ -244,7 +246,7 @@ class AndroidVideoGenerator {
mediaMuxer: MediaMuxer,
audioSources: List,
fps: Int,
- audioTrackIndices: List
+ audioTrackIndices: List,
) {
Log.d(TAG, "muxAudioTracks: adding audio")
val bufferSize = 1 * 1024 * 1024
@@ -310,7 +312,7 @@ class AndroidVideoGenerator {
fps: Int,
initialPresentationTimeUs: Long,
audioTrackFormats: List,
- muxerAudioTrackIndices: MutableList
+ muxerAudioTrackIndices: MutableList,
) {
var localMuxerStarted = muxerStarted
var localVideoTrackIndex = videoTrackIndex
@@ -319,7 +321,9 @@ class AndroidVideoGenerator {
while (true) {
val encoderStatus = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC)
when {
- encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> break
+ encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> {
+ break
+ }
encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
// This can happen if no encoded output was dequeued earlier.
@@ -336,14 +340,17 @@ class AndroidVideoGenerator {
localMuxerStarted = true
}
- encoderStatus < 0 -> Log.w(
- TAG,
- "unexpected result from encoder.dequeueOutputBuffer (during drain): $encoderStatus"
- )
+ encoderStatus < 0 -> {
+ Log.w(
+ TAG,
+ "unexpected result from encoder.dequeueOutputBuffer (during drain): $encoderStatus",
+ )
+ }
else -> {
- val encodedData = mediaCodec.getOutputBuffer(encoderStatus)
- ?: throw RuntimeException("encoderOutputBuffer $encoderStatus was null (during drain)")
+ val encodedData =
+ mediaCodec.getOutputBuffer(encoderStatus)
+ ?: throw RuntimeException("encoderOutputBuffer $encoderStatus was null (during drain)")
if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
bufferInfo.size = 0
@@ -371,38 +378,47 @@ class AndroidVideoGenerator {
}
}
- private fun calculateBitRate(width: Int, height: Int, frameRate: Int): Int {
- return (width * height * frameRate * 0.25).toInt()
- }
+ private fun calculateBitRate(
+ width: Int,
+ height: Int,
+ frameRate: Int,
+ ): Int = (width * height * frameRate * 0.25).toInt()
private fun getBitmapCount(
- bitmaps: List = mutableListOf(), inputDir: File? = null
- ): Int = if (inputDir != null) {
- initBitmapFiles(inputDir)
- bitmapFiles?.size ?: 0
- } else {
- bitmaps.size
- }
+ bitmaps: List = mutableListOf(),
+ inputDir: File? = null,
+ ): Int =
+ if (inputDir != null) {
+ initBitmapFiles(inputDir)
+ bitmapFiles?.size ?: 0
+ } else {
+ bitmaps.size
+ }
private fun getBitmap(
- bitmaps: List = mutableListOf(), inputDir: File? = null, index: Int
- ): Bitmap? = if (inputDir != null) {
- initBitmapFiles(inputDir)
+ bitmaps: List = mutableListOf(),
+ inputDir: File? = null,
+ index: Int,
+ ): Bitmap? =
+ if (inputDir != null) {
+ initBitmapFiles(inputDir)
- bitmapFiles?.getOrNull(index)?.let { file ->
- BitmapFactory.decodeFile(file.absolutePath)
+ bitmapFiles?.getOrNull(index)?.let { file ->
+ BitmapFactory.decodeFile(file.absolutePath)
+ }
+ } else {
+ bitmaps.getOrNull(index)
}
- } else {
- bitmaps.getOrNull(index)
- }
private fun initBitmapFiles(inputDir: File) {
if (bitmapFiles == null) {
- bitmapFiles = inputDir.listFiles { file ->
- file.extension.lowercase() in listOf("png", "jpg", "jpeg", "webp")
- }?.sortedBy { file ->
- file.nameWithoutExtension.filter { it.isDigit() }.toIntOrNull() ?: 0
- }
+ bitmapFiles =
+ inputDir
+ .listFiles { file ->
+ file.extension.lowercase() in listOf("png", "jpg", "jpeg", "webp")
+ }?.sortedBy { file ->
+ file.nameWithoutExtension.filter { it.isDigit() }.toIntOrNull() ?: 0
+ }
}
}
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AudioProcessor.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AudioProcessor.kt
index 10acb8b0..cb964273 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AudioProcessor.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/infra/AudioProcessor.kt
@@ -42,12 +42,20 @@ fun extractWaveformFromFile(file: File): List {
if (sampleSize < 0) {
codec.queueInputBuffer(
- inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
+ inputIndex,
+ 0,
+ 0,
+ 0,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM,
)
isEOS = true
} else {
codec.queueInputBuffer(
- inputIndex, 0, sampleSize, extractor.sampleTime, 0
+ inputIndex,
+ 0,
+ sampleSize,
+ extractor.sampleTime,
+ 0,
)
extractor.advance()
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseContourMotionView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseContourMotionView.kt
index e2eae4a9..94e9a974 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseContourMotionView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseContourMotionView.kt
@@ -14,8 +14,9 @@ open class BaseContourMotionView(
override val startFrame: Int,
override val endFrame: Int,
override val loop: Pair = Pair(0, 0),
- override val effects: List = emptyList()
-) : ContourLayout(context), MotionView {
+ override val effects: List = emptyList(),
+) : ContourLayout(context),
+ MotionView {
companion object {
private const val TAG = "MotionView"
}
@@ -46,4 +47,4 @@ open class BaseContourMotionView(
}
override fun getViewBitmap() = this.toBitmap()
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseFrameMotionView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseFrameMotionView.kt
index 7d38603a..f20f58f4 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseFrameMotionView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/BaseFrameMotionView.kt
@@ -12,77 +12,89 @@ import com.tejpratapsingh.motionlib.core.MotionEffect
import com.tejpratapsingh.motionlib.core.MotionView
import com.tejpratapsingh.motionlib.core.extensions.toBitmap
-abstract class BaseFrameMotionView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr), MotionView {
+abstract class BaseFrameMotionView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ ) : FrameLayout(context, attrs, defStyleAttr),
+ MotionView {
+ override var startFrame: Int = 0
+ override var endFrame: Int = 0
+ override var loop: Pair = Pair(0, 0)
- override var startFrame: Int = 0
- override var endFrame: Int = 0
- override var loop: Pair = Pair(0, 0)
-
- companion object {
- private const val TAG = "MotionView"
- }
-
- init {
- context.theme.obtainStyledAttributes(
- attrs,
- R.styleable.BaseFrameMotionView,
- defStyleAttr,
- 0
- ).apply {
- try {
- startFrame = getInt(R.styleable.BaseFrameMotionView_startFrame, 0)
- endFrame = getInt(R.styleable.BaseFrameMotionView_endFrame, 0)
- val loopStart = getInt(R.styleable.BaseFrameMotionView_loopStart, 0)
- val loopEnd = getInt(R.styleable.BaseFrameMotionView_loopEnd, 0)
- loop = Pair(loopStart, loopEnd)
- } finally {
- recycle()
- }
+ companion object {
+ private const val TAG = "MotionView"
}
- }
- @CallSuper
- override fun forFrame(frame: Int): MotionView {
- if (frame < startFrame) {
- visibility = INVISIBLE
- return this
- }
- if (frame > endFrame) {
- visibility = INVISIBLE
- return this
+ init {
+ context.theme
+ .obtainStyledAttributes(
+ attrs,
+ R.styleable.BaseFrameMotionView,
+ defStyleAttr,
+ 0,
+ ).apply {
+ try {
+ startFrame = getInt(R.styleable.BaseFrameMotionView_startFrame, 0)
+ endFrame = getInt(R.styleable.BaseFrameMotionView_endFrame, 0)
+ val loopStart = getInt(R.styleable.BaseFrameMotionView_loopStart, 0)
+ val loopEnd = getInt(R.styleable.BaseFrameMotionView_loopEnd, 0)
+ loop = Pair(loopStart, loopEnd)
+ } finally {
+ recycle()
+ }
+ }
}
- visibility = VISIBLE
- Log.d(TAG, "forFrame: isVisible: $isVisible")
+ @CallSuper
+ override fun forFrame(frame: Int): MotionView {
+ if (frame < startFrame) {
+ visibility = INVISIBLE
+ return this
+ }
+ if (frame > endFrame) {
+ visibility = INVISIBLE
+ return this
+ }
+ visibility = VISIBLE
+
+ Log.d(TAG, "forFrame: isVisible: $isVisible")
- for (i in 0 until this.childCount) {
- val view = this.getChildAt(i)
+ for (i in 0 until this.childCount) {
+ val view = this.getChildAt(i)
- if (view is MotionView) {
- view.forFrame(frame)
+ if (view is MotionView) {
+ view.forFrame(frame)
+ }
}
- }
- return this
- }
+ return this
+ }
- override fun getViewBitmap() = this.toBitmap()
+ override fun getViewBitmap() = this.toBitmap()
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- val desiredWidth = MotionConfig.aspectRatio.width
- val desiredHeight = MotionConfig.aspectRatio.height
- setMeasuredDimension(desiredWidth, desiredHeight)
- getChildAt(0)?.measure(
- MeasureSpec.makeMeasureSpec(desiredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY)
- )
- }
+ override fun onMeasure(
+ widthMeasureSpec: Int,
+ heightMeasureSpec: Int,
+ ) {
+ val desiredWidth = MotionConfig.aspectRatio.width
+ val desiredHeight = MotionConfig.aspectRatio.height
+ setMeasuredDimension(desiredWidth, desiredHeight)
+ getChildAt(0)?.measure(
+ MeasureSpec.makeMeasureSpec(desiredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY),
+ )
+ }
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- getChildAt(0)?.layout(0, 0, r - l, b - t)
+ override fun onLayout(
+ changed: Boolean,
+ l: Int,
+ t: Int,
+ r: Int,
+ b: Int,
+ ) {
+ getChildAt(0)?.layout(0, 0, r - l, b - t)
+ }
}
-}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IComposerView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IComposerView.kt
index 10727752..09617baa 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IComposerView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IComposerView.kt
@@ -4,4 +4,4 @@ import com.tejpratapsingh.motionlib.core.MotionPlugin
interface IComposerView {
val plugins: List
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IMotionVideoProducer.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IMotionVideoProducer.kt
index 598c67f2..fcfeaa76 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IMotionVideoProducer.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/IMotionVideoProducer.kt
@@ -8,9 +8,10 @@ import java.io.File
interface IMotionVideoProducer {
fun addMotionViewToSequence(motionView: T): MotionVideoProducer where T : MotionView, T : ViewGroup
+
suspend fun produceVideo(
context: Context,
outputFile: File,
- progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)? = null
+ progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)? = null,
): File
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionComposerView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionComposerView.kt
index 179f876b..a8c91476 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionComposerView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/MotionComposerView.kt
@@ -15,9 +15,10 @@ open class MotionComposerView(
override val startFrame: Int = 0,
override val endFrame: Int = 0,
override val plugins: List,
- override val loop: Pair = Pair(0, 0)
-) : ContourLayout(context), MotionView, IComposerView {
-
+ override val loop: Pair = Pair(0, 0),
+) : ContourLayout(context),
+ MotionView,
+ IComposerView {
override val effects: List = emptyList()
companion object {
@@ -41,17 +42,17 @@ open class MotionComposerView(
return this
}
- fun runEffects(view: MotionView, frame: Int) {
- return view.effects.forEach { effect ->
- effect.forFrame(frame)
- }
+ fun runEffects(
+ view: MotionView,
+ frame: Int,
+ ) = view.effects.forEach { effect ->
+ effect.forFrame(frame)
}
- override fun getViewBitmap(): Bitmap {
- return toBitmap().let {
+ override fun getViewBitmap(): Bitmap =
+ toBitmap().let {
plugins.fold(it) { acc, plugin ->
plugin.apply(acc)
}
}
- }
-}
\ No newline at end of file
+}
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 924e0771..d9f89ade 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
@@ -18,7 +18,7 @@ open class MotionVideoProducer(
val motionConfig: MotionConfig,
val videoProducerAdapter: VideoProducerAdapter,
val motionComposerView: MotionComposerView,
- val motionAudio: List = emptyList()
+ val motionAudio: List = emptyList(),
) : IMotionVideoProducer {
var totalFrames: Int = 0
private set
@@ -37,10 +37,12 @@ open class MotionVideoProducer(
context = context,
motionConfig = config,
videoProducerAdapter = videoProducerAdapter,
- motionComposerView = MotionComposerView(
- context = context, plugins = plugins
- ),
- motionAudio = motionAudio
+ motionComposerView =
+ MotionComposerView(
+ context = context,
+ plugins = plugins,
+ ),
+ motionAudio = motionAudio,
).also {
MotionConfig.aspectRatio = config.aspectRatio
MotionConfig.fps = config.fps
@@ -51,11 +53,16 @@ open class MotionVideoProducer(
override fun addMotionViewToSequence(motionView: T): MotionVideoProducer where T : MotionView, T : ViewGroup {
totalFrames = maxOf(totalFrames, motionView.endFrame)
motionComposerView.apply {
- motionView.layoutBy(x = centerHorizontallyTo {
- parent.centerX()
- }, y = centerVerticallyTo {
- parent.centerY()
- })
+ motionView.layoutBy(
+ x =
+ centerHorizontallyTo {
+ parent.centerX()
+ },
+ y =
+ centerVerticallyTo {
+ parent.centerY()
+ },
+ )
}
return this
}
@@ -63,22 +70,24 @@ open class MotionVideoProducer(
override suspend fun produceVideo(
context: Context,
outputFile: File,
- progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?
- ): File = withContext(Dispatchers.IO) { // Use Dispatchers.Default for CPU-bound work
- if (outputFile.exists()) {
- outputFile.delete()
- }
+ progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?,
+ ): File =
+ withContext(Dispatchers.IO) {
+ // Use Dispatchers.Default for CPU-bound work
+ if (outputFile.exists()) {
+ outputFile.delete()
+ }
- videoProducerAdapter.produceVideo(
- context = context,
- motionConfig = motionConfig,
- motionComposerView = motionComposerView,
- motionAudio = motionAudio,
- totalFrames = totalFrames,
- outputFile = outputFile,
- progressListener = progressListener
- )
+ videoProducerAdapter.produceVideo(
+ context = context,
+ motionConfig = motionConfig,
+ motionComposerView = motionComposerView,
+ motionAudio = motionAudio,
+ totalFrames = totalFrames,
+ outputFile = outputFile,
+ progressListener = progressListener,
+ )
- outputFile
- }
-}
\ No newline at end of file
+ outputFile
+ }
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/OrientedMotionView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/OrientedMotionView.kt
index ace2ad26..c559b1a9 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/OrientedMotionView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/core/motion/OrientedMotionView.kt
@@ -5,5 +5,5 @@ import android.content.Context
open class OrientedMotionView(
context: Context,
startFrame: Int,
- endFrame: Int
-) : BaseContourMotionView(context, startFrame, endFrame)
\ No newline at end of file
+ endFrame: Int,
+) : BaseContourMotionView(context, startFrame, endFrame)
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/MotionVideoPlayer.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/MotionVideoPlayer.kt
index e34e21e3..11730745 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/MotionVideoPlayer.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/MotionVideoPlayer.kt
@@ -17,9 +17,10 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-class MotionVideoPlayer(context: Context, private val motionVideoProducer: MotionVideoProducer) :
- ContourLayout(context) {
-
+class MotionVideoPlayer(
+ context: Context,
+ private val motionVideoProducer: MotionVideoProducer,
+) : ContourLayout(context) {
companion object {
private const val TAG = "MotionVideoPlayer"
}
@@ -33,75 +34,93 @@ class MotionVideoPlayer(context: Context, private val motionVideoProducer: Motio
private val activePlayers = mutableMapOf()
- val seekBar: SeekBar = SeekBar(context).apply {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- min = 1
- } // Start from 1 to avoid confusion with frame 0
- max = motionVideoProducer.totalFrames
+ val seekBar: SeekBar =
+ SeekBar(context).apply {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ min = 1
+ } // Start from 1 to avoid confusion with frame 0
+ max = motionVideoProducer.totalFrames
+
+ setOnSeekBarChangeListener(
+ object : SeekBar.OnSeekBarChangeListener {
+ override fun onProgressChanged(
+ seekBar: SeekBar?,
+ progress: Int,
+ fromUser: Boolean,
+ ) {
+ if (fromUser) { // Only update preview if change is from user interaction
+ motionVideoProducer.motionComposerView.forFrame(progress)
+ }
+ }
- setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
- override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
- if (fromUser) { // Only update preview if change is from user interaction
- motionVideoProducer.motionComposerView.forFrame(progress)
- }
- }
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {
+ // Optional: Pause playback when user starts dragging
+ if (isPlaying) {
+ pausePlayback()
+ }
+ }
- override fun onStartTrackingTouch(seekBar: SeekBar?) {
- // Optional: Pause playback when user starts dragging
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {
+ // Optional: Resume playback or leave it paused
+ }
+ },
+ )
+ }
+
+ private val playPauseButton: ImageButton =
+ ImageButton(context).apply {
+ setImageResource(android.R.drawable.ic_media_play)
+ setOnClickListener {
if (isPlaying) {
pausePlayback()
+ } else {
+ startPlayback()
}
}
-
- override fun onStopTrackingTouch(seekBar: SeekBar?) {
- // Optional: Resume playback or leave it paused
- }
- })
- }
-
- private val playPauseButton: ImageButton = ImageButton(context).apply {
- setImageResource(android.R.drawable.ic_media_play)
- setOnClickListener {
- if (isPlaying) {
- pausePlayback()
- } else {
- startPlayback()
- }
}
- }
- private val controlsLayout: LinearLayoutCompat = LinearLayoutCompat(context).apply {
- orientation = LinearLayoutCompat.HORIZONTAL
- gravity = android.view.Gravity.CENTER_VERTICAL // Center items in controls
- }
+ private val controlsLayout: LinearLayoutCompat =
+ LinearLayoutCompat(context).apply {
+ orientation = LinearLayoutCompat.HORIZONTAL
+ gravity = android.view.Gravity.CENTER_VERTICAL // Center items in controls
+ }
- private val previewLayout: LinearLayoutCompat = LinearLayoutCompat(context).apply {
- orientation = LinearLayoutCompat.VERTICAL
- gravity = android.view.Gravity.CENTER // Center preview
- }
+ private val previewLayout: LinearLayoutCompat =
+ LinearLayoutCompat(context).apply {
+ orientation = LinearLayoutCompat.VERTICAL
+ gravity = android.view.Gravity.CENTER // Center preview
+ }
init {
controlsLayout.addView(playPauseButton)
controlsLayout.addView(seekBar)
- val seekBarParams = LinearLayoutCompat.LayoutParams(
- 0, LinearLayoutCompat.LayoutParams.WRAP_CONTENT
- ).apply {
- weight = 1f
- }
+ val seekBarParams =
+ LinearLayoutCompat
+ .LayoutParams(
+ 0,
+ LinearLayoutCompat.LayoutParams.WRAP_CONTENT,
+ ).apply {
+ weight = 1f
+ }
seekBar.layoutParams = seekBarParams
controlsLayout.layoutBy(
x = leftTo { parent.left() }.rightTo { parent.right() },
- y = bottomTo { parent.bottom() })
+ y = bottomTo { parent.bottom() },
+ )
previewLayout.addView(motionVideoProducer.motionComposerView)
previewLayout.layoutBy(
x = leftTo { parent.left() }.rightTo { parent.right() },
- y = topTo { parent.top() }.bottomTo { controlsLayout.top() })
+ y = topTo { parent.top() }.bottomTo { controlsLayout.top() },
+ )
}
- private fun startAudioIfNeeded(frame: Int, motionAudios: List) {
+ private fun startAudioIfNeeded(
+ frame: Int,
+ motionAudios: List,
+ ) {
motionAudios.forEach { audio ->
val shouldPlay = frame in audio.delayFrame..audio.endFrame
val player = activePlayers[audio]
@@ -109,11 +128,12 @@ class MotionVideoPlayer(context: Context, private val motionVideoProducer: Motio
if (shouldPlay) {
if (player == null) {
// First time: create player
- val mediaPlayer = android.media.MediaPlayer().apply {
- setDataSource(audio.file.absolutePath)
- prepare()
- start()
- }
+ val mediaPlayer =
+ android.media.MediaPlayer().apply {
+ setDataSource(audio.file.absolutePath)
+ prepare()
+ start()
+ }
activePlayers[audio] = mediaPlayer
} else if (!player.isPlaying) {
// Resume instead of restarting
@@ -155,28 +175,29 @@ class MotionVideoPlayer(context: Context, private val motionVideoProducer: Motio
playPauseButton.setImageResource(android.R.drawable.ic_media_pause)
// Launch a new coroutine for playback
- playbackJob = scope.launch {
- while (isPlaying) {
- var currentProgress = seekBar.progress
- if (currentProgress < seekBar.max) {
- currentProgress++
- seekBar.progress = currentProgress
- motionVideoProducer.motionComposerView.forFrame(currentProgress)
-
- // 🔊 Check if we should play audio
- startAudioIfNeeded(
- frame = currentProgress,
- motionAudios = motionVideoProducer.motionAudio // you need to expose this
- )
-
- delay(playbackDelayMs)
- } else {
- seekBar.progress = 0
- motionVideoProducer.motionComposerView.forFrame(0)
- stopAllAudio()
+ playbackJob =
+ scope.launch {
+ while (isPlaying) {
+ var currentProgress = seekBar.progress
+ if (currentProgress < seekBar.max) {
+ currentProgress++
+ seekBar.progress = currentProgress
+ motionVideoProducer.motionComposerView.forFrame(currentProgress)
+
+ // 🔊 Check if we should play audio
+ startAudioIfNeeded(
+ frame = currentProgress,
+ motionAudios = motionVideoProducer.motionAudio, // you need to expose this
+ )
+
+ delay(playbackDelayMs)
+ } else {
+ seekBar.progress = 0
+ motionVideoProducer.motionComposerView.forFrame(0)
+ stopAllAudio()
+ }
}
}
- }
}
private fun pausePlayback() {
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/CutoutTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/CutoutTextView.kt
index d9e3d469..36f30529 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/CutoutTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/CutoutTextView.kt
@@ -9,38 +9,39 @@ import android.graphics.PorterDuffXfermode
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
-internal class CutoutTextView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : AppCompatTextView(context, attrs, defStyle) {
-
- private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
- private val porterDuffMode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
-
- override fun onDraw(canvas: Canvas) {
- // Draw the background image first
- val bgDrawable = background
- bgDrawable?.setBounds(0, 0, width, height)
- bgDrawable?.draw(canvas)
-
- // Create a new layer for cutout
- val saveCount = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)
-
- // Fill with black overlay
- canvas.drawColor(Color.BLACK)
-
- // Cut out text
- textPaint.typeface = typeface
- textPaint.textSize = textSize
- textPaint.textAlign = Paint.Align.CENTER
- textPaint.xfermode = porterDuffMode
-
- val xPos = width / 2f
- val yPos = height / 2f - (textPaint.descent() + textPaint.ascent()) / 2
- canvas.drawText(text.toString(), xPos, yPos, textPaint)
-
- textPaint.xfermode = null
- canvas.restoreToCount(saveCount)
+internal class CutoutTextView
+ @JvmOverloads
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0,
+ ) : AppCompatTextView(context, attrs, defStyle) {
+ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private val porterDuffMode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
+
+ override fun onDraw(canvas: Canvas) {
+ // Draw the background image first
+ val bgDrawable = background
+ bgDrawable?.setBounds(0, 0, width, height)
+ bgDrawable?.draw(canvas)
+
+ // Create a new layer for cutout
+ val saveCount = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null)
+
+ // Fill with black overlay
+ canvas.drawColor(Color.BLACK)
+
+ // Cut out text
+ textPaint.typeface = typeface
+ textPaint.textSize = textSize
+ textPaint.textAlign = Paint.Align.CENTER
+ textPaint.xfermode = porterDuffMode
+
+ val xPos = width / 2f
+ val yPos = height / 2f - (textPaint.descent() + textPaint.ascent()) / 2
+ canvas.drawText(text.toString(), xPos, yPos, textPaint)
+
+ textPaint.xfermode = null
+ canvas.restoreToCount(saveCount)
+ }
}
-}
\ No newline at end of file
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/BaseAudioWaveformView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/BaseAudioWaveformView.kt
index 0f860ddd..94dd19e2 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/BaseAudioWaveformView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/BaseAudioWaveformView.kt
@@ -4,21 +4,24 @@ import android.content.Context
import android.graphics.Paint
import com.tejpratapsingh.motionlib.core.motion.BaseContourMotionView
-open class BaseAudioWaveformView(context: Context,
- override var startFrame: Int,
- override var endFrame: Int
+open class BaseAudioWaveformView(
+ context: Context,
+ override var startFrame: Int,
+ override var endFrame: Int,
) : BaseContourMotionView(context, startFrame, endFrame) {
protected var currentFrame: Int = 0
- protected val paint = Paint().apply {
- color = 0xFF009688.toInt() // Teal spikes
- strokeWidth = 3f
- isAntiAlias = true
- }
+ protected val paint =
+ Paint().apply {
+ color = 0xFF009688.toInt() // Teal spikes
+ strokeWidth = 3f
+ isAntiAlias = true
+ }
- protected val cursorPaint = Paint().apply {
- color = 0xFFFF5722.toInt() // Orange playback cursor
- strokeWidth = 5f
- isAntiAlias = true
- }
-}
\ No newline at end of file
+ protected val cursorPaint =
+ Paint().apply {
+ color = 0xFFFF5722.toInt() // Orange playback cursor
+ strokeWidth = 5f
+ isAntiAlias = true
+ }
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/CircularAudioWaveformView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/CircularAudioWaveformView.kt
index 2d0176e4..2db8b704 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/CircularAudioWaveformView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/CircularAudioWaveformView.kt
@@ -12,9 +12,8 @@ class CircularAudioWaveformView(
context: Context,
private val amplitudes: List = emptyList(),
override var startFrame: Int,
- override var endFrame: Int
+ override var endFrame: Int,
) : BaseAudioWaveformView(context, startFrame, endFrame) {
-
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (amplitudes.isEmpty() || startFrame >= endFrame) return
@@ -49,4 +48,4 @@ class CircularAudioWaveformView(
invalidate()
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/RadialAudioWaveformView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/RadialAudioWaveformView.kt
index e6844a1a..6e8d739f 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/RadialAudioWaveformView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/audio/RadialAudioWaveformView.kt
@@ -14,9 +14,8 @@ class RadialAudioWaveformView(
context: Context,
private val amplitudes: List = emptyList(),
override var startFrame: Int,
- override var endFrame: Int
+ override var endFrame: Int,
) : BaseAudioWaveformView(context, startFrame, endFrame) {
-
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (amplitudes.isEmpty() || startFrame >= endFrame) return
@@ -56,4 +55,4 @@ class RadialAudioWaveformView(
invalidate()
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/background/GradientView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/background/GradientView.kt
index e1f3a378..b9546f9c 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/background/GradientView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/background/GradientView.kt
@@ -18,7 +18,7 @@ import com.tejpratapsingh.motionlib.core.motion.OrientedMotionView
enum class Orientation {
HORIZONTAL,
VERTICAL,
- CIRCULAR
+ CIRCULAR,
}
class GradientView(
@@ -26,19 +26,20 @@ class GradientView(
startFrame: Int,
endFrame: Int,
private val orientation: Orientation,
- private val colors: IntArray
+ private val colors: IntArray,
) : OrientedMotionView(
- context = context,
- startFrame = startFrame,
- endFrame = endFrame
-) {
+ context = context,
+ startFrame = startFrame,
+ endFrame = endFrame,
+ ) {
private companion object {
// const val TAG = "GradientView" // For logging if needed
}
- private val paint: Paint = Paint().apply {
- isAntiAlias = true // Good practice for smoother gradients
- }
+ private val paint: Paint =
+ Paint().apply {
+ isAntiAlias = true // Good practice for smoother gradients
+ }
private var currentFrame: Int = 0
private val interpolator: Interpolators =
@@ -66,7 +67,12 @@ class GradientView(
}
}
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ override fun onSizeChanged(
+ w: Int,
+ h: Int,
+ oldw: Int,
+ oldh: Int,
+ ) {
super.onSizeChanged(w, h, oldw, oldh)
// If valueRange depends on the view's actual dimensions:
/*
@@ -75,7 +81,7 @@ class GradientView(
Orientation.VERTICAL -> Pair(first = 0f, second = h.toFloat().coerceAtLeast(1f))
Orientation.HORIZONTAL -> Pair(first = 0f, second = w.toFloat().coerceAtLeast(1f))
}
- */
+ */
gradientShader = null // Invalidate shader if size changes affect it
}
@@ -98,46 +104,54 @@ class GradientView(
// Log.d(TAG, "onDraw: called : $currentFrame, width: $width, height: $height, valueRange: $valueRange")
// }
- val interpolatedValue: Float = MotionInterpolator.interpolateForRange(
- interpolator = interpolator,
- currentFrame = currentFrame,
- frameRange = frameRange,
- valueRange = valueRange
- )
+ val interpolatedValue: Float =
+ MotionInterpolator.interpolateForRange(
+ interpolator = interpolator,
+ currentFrame = currentFrame,
+ frameRange = frameRange,
+ valueRange = valueRange,
+ )
// Only recreate shader if necessary (value changed or not created yet)
if (gradientShader == null || interpolatedValue != lastInterpolatedValue) {
lastInterpolatedValue = interpolatedValue
- gradientShader = when (orientation) {
- Orientation.CIRCULAR -> RadialGradient(
- (width / 2).toFloat(),
- (height / 2).toFloat(),
- interpolatedValue.coerceAtLeast(0.1f), // Ensure radius is positive
- colors,
- null, // Positions: null means evenly distributed
- Shader.TileMode.CLAMP
- )
-
- Orientation.VERTICAL -> LinearGradient(
- 0f,
- 0f,
- 0f,
- interpolatedValue.coerceAtLeast(0.1f), // Ensure height is positive
- colors,
- null,
- Shader.TileMode.CLAMP
- )
-
- Orientation.HORIZONTAL -> LinearGradient(
- 0f,
- 0f,
- interpolatedValue.coerceAtLeast(0.1f), // Ensure width is positive
- 0f,
- colors,
- null,
- Shader.TileMode.CLAMP
- )
- }
+ gradientShader =
+ when (orientation) {
+ Orientation.CIRCULAR -> {
+ RadialGradient(
+ (width / 2).toFloat(),
+ (height / 2).toFloat(),
+ interpolatedValue.coerceAtLeast(0.1f), // Ensure radius is positive
+ colors,
+ null, // Positions: null means evenly distributed
+ Shader.TileMode.CLAMP,
+ )
+ }
+
+ Orientation.VERTICAL -> {
+ LinearGradient(
+ 0f,
+ 0f,
+ 0f,
+ interpolatedValue.coerceAtLeast(0.1f), // Ensure height is positive
+ colors,
+ null,
+ Shader.TileMode.CLAMP,
+ )
+ }
+
+ Orientation.HORIZONTAL -> {
+ LinearGradient(
+ 0f,
+ 0f,
+ interpolatedValue.coerceAtLeast(0.1f), // Ensure width is positive
+ 0f,
+ colors,
+ null,
+ Shader.TileMode.CLAMP,
+ )
+ }
+ }
paint.shader = gradientShader
}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/container/RotatingMotionView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/container/RotatingMotionView.kt
index f8088ae9..87f5234a 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/container/RotatingMotionView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/container/RotatingMotionView.kt
@@ -10,20 +10,22 @@ class RotatingMotionView(
startFrame: Int,
endFrame: Int,
view: View,
- private val degreePerSecond: Float = 6f
+ private val degreePerSecond: Float = 6f,
) : BaseContourMotionView(context, startFrame, endFrame) {
-
init {
view.layoutBy(
- x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- }, y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- }
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
)
}
@@ -39,4 +41,4 @@ class RotatingMotionView(
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TransparentTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TransparentTextView.kt
index a28f86e7..7ba619f4 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TransparentTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TransparentTextView.kt
@@ -6,14 +6,17 @@ import com.tejpratapsingh.motionlib.ui.custom.CutoutTextView
import com.tejpratapsingh.motionlib.ui.custom.text.abstract.AbstractMotionTextView
class TransparentTextView(
- context: Context, private val text: String, startFrame: Int, endFrame: Int
+ context: Context,
+ private val text: String,
+ startFrame: Int,
+ endFrame: Int,
) : AbstractMotionTextView(
- context,
- text,
- startFrame,
- endFrame,
- textView = CutoutTextView(context)
-) {
+ context,
+ text,
+ startFrame,
+ endFrame,
+ textView = CutoutTextView(context),
+ ) {
private val TAG by lazy {
"TransparentTextView"
}
@@ -25,4 +28,4 @@ class TransparentTextView(
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TypeWriterTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TypeWriterTextView.kt
index e77bff2e..0f47b7ed 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TypeWriterTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/TypeWriterTextView.kt
@@ -19,7 +19,7 @@ class TypeWriterTextView(
private val text: String,
startFrame: Int,
endFrame: Int,
- textView: AppCompatTextView = CutoutTextView(context)
+ textView: AppCompatTextView = CutoutTextView(context),
) : AbstractMotionTextView(context, text, startFrame, endFrame, textView) {
private val TAG by lazy {
"TypeWriterTextView"
@@ -28,12 +28,14 @@ class TypeWriterTextView(
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val visibleCharsCount: Int = MotionInterpolator.interpolateForRange(
- Interpolators(Easings.LINEAR),
- frame,
- Pair(startFrame, endFrame),
- Pair(0f, text.length.toFloat())
- ).toInt()
+ val visibleCharsCount: Int =
+ MotionInterpolator
+ .interpolateForRange(
+ Interpolators(Easings.LINEAR),
+ frame,
+ Pair(startFrame, endFrame),
+ Pair(0f, text.length.toFloat()),
+ ).toInt()
Log.d(TAG, "visibleCharsCount: $visibleCharsCount")
@@ -42,10 +44,10 @@ class TypeWriterTextView(
ForegroundColorSpan(Color.TRANSPARENT),
maxOf(0, visibleCharsCount),
text.length,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
textView.text = spannableString
textView.invalidate()
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordBlinkTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordBlinkTextView.kt
index aad32ab0..4ccfbc2b 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordBlinkTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordBlinkTextView.kt
@@ -18,7 +18,7 @@ class WordBlinkTextView(
text: String,
startFrame: Int = 0,
endFrame: Int = -1,
- textView: AppCompatTextView = CutoutTextView(context)
+ textView: AppCompatTextView = CutoutTextView(context),
) : AbstractMotionTextView(context, text, startFrame, endFrame, textView) {
private val TAG by lazy {
"WordBlinkTextView"
@@ -34,19 +34,21 @@ class WordBlinkTextView(
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val visibleWordCount: Int = MotionInterpolator.interpolateForRange(
- Interpolators(Easings.LINEAR),
- frame,
- Pair(startFrame, endFrame),
- Pair(0f, wordCount.toFloat())
- ).toInt()
+ val visibleWordCount: Int =
+ MotionInterpolator
+ .interpolateForRange(
+ Interpolators(Easings.LINEAR),
+ frame,
+ Pair(startFrame, endFrame),
+ Pair(0f, wordCount.toFloat()),
+ ).toInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
(textView as TextView).setAutoSizeTextTypeUniformWithConfiguration(
12,
100,
1,
- TypedValue.COMPLEX_UNIT_SP
+ TypedValue.COMPLEX_UNIT_SP,
)
}
@@ -57,4 +59,4 @@ class WordBlinkTextView(
return this
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordWriterTextView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordWriterTextView.kt
index d21ecc6f..7de84bb3 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordWriterTextView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/text/WordWriterTextView.kt
@@ -19,7 +19,7 @@ class WordWriterTextView(
private val text: String,
startFrame: Int = 0,
endFrame: Int = -1,
- textView: AppCompatTextView = CutoutTextView(context)
+ textView: AppCompatTextView = CutoutTextView(context),
) : AbstractMotionTextView(context, text, startFrame, endFrame, textView) {
private val TAG by lazy {
"WordWriterTextView"
@@ -31,12 +31,14 @@ class WordWriterTextView(
override fun forFrame(frame: Int): MotionView {
super.forFrame(frame)
- val visibleWordCount: Int = MotionInterpolator.interpolateForRange(
- Interpolators(Easings.LINEAR),
- frame,
- Pair(startFrame, endFrame),
- Pair(0f, wordCount.toFloat())
- ).toInt()
+ val visibleWordCount: Int =
+ MotionInterpolator
+ .interpolateForRange(
+ Interpolators(Easings.LINEAR),
+ frame,
+ Pair(startFrame, endFrame),
+ Pair(0f, wordCount.toFloat()),
+ ).toInt()
Log.d(TAG, "visibleWordCount: $visibleWordCount")
val visibleCharacters = wordArray.subList(0, visibleWordCount).joinToString(" ").length
@@ -46,11 +48,11 @@ class WordWriterTextView(
ForegroundColorSpan(Color.TRANSPARENT),
maxOf(0, visibleCharacters),
text.length,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
textView.text = spannableString
textView.invalidate()
return this
}
-}
\ No newline at end of file
+}
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 87d9d921..3f970331 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
@@ -11,9 +11,8 @@ abstract class AbstractMotionTextView(
startFrame: Int,
endFrame: Int,
val textView: AppCompatTextView,
- fontUrl: String? = null
+ fontUrl: String? = null,
) : BaseContourMotionView(context, startFrame, endFrame) {
-
init {
textView.apply {
if (fontUrl != null) {
@@ -21,15 +20,20 @@ abstract class AbstractMotionTextView(
}
}
- textView.layoutBy(x = leftTo {
- parent.left()
- }.rightTo {
- parent.right()
- }, y = topTo {
- parent.top()
- }.bottomTo {
- parent.bottom()
- })
+ textView.layoutBy(
+ x =
+ leftTo {
+ parent.left()
+ }.rightTo {
+ parent.right()
+ },
+ y =
+ topTo {
+ parent.top()
+ }.bottomTo {
+ parent.bottom()
+ },
+ )
textView.text = text
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/VideoFrameView.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/VideoFrameView.kt
index e9b6be8c..1f6653a0 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/VideoFrameView.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/custom/video/VideoFrameView.kt
@@ -12,22 +12,29 @@ import com.tejpratapsingh.motionlib.utils.getVideoFpsWithRetriever
import kotlin.math.roundToLong
class VideoFrameView(
- context: Context, videoUri: Uri, startFrame: Int, endFrame: Int
+ context: Context,
+ videoUri: Uri,
+ startFrame: Int,
+ endFrame: Int,
) : BaseContourMotionView(context, startFrame, endFrame) {
-
val fps = getVideoFpsWithRetriever(context, videoUri) ?: 30F
- val videoBitmaps = extractAllVideoFrames(
- context = context, videoUri = videoUri, frameIntervalUs = (1_000_000 / fps).roundToLong()
- )
+ val videoBitmaps =
+ extractAllVideoFrames(
+ context = context,
+ videoUri = videoUri,
+ frameIntervalUs = (1_000_000 / fps).roundToLong(),
+ )
- private val imageView = ImageView(context).apply {
- scaleType = ImageView.ScaleType.CENTER_CROP
- }
+ private val imageView =
+ ImageView(context).apply {
+ scaleType = ImageView.ScaleType.CENTER_CROP
+ }
init {
imageView.layoutBy(
x = leftTo { parent.left() }.rightTo { parent.right() },
- y = topTo { parent.top() }.bottomTo { parent.bottom() })
+ y = topTo { parent.top() }.bottomTo { parent.bottom() },
+ )
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
contourHeightOf {
@@ -44,10 +51,10 @@ class VideoFrameView(
super.forFrame(frame)
currentFrameBitmap = videoBitmaps.getOrNull(frame - startFrame) ?: videoBitmaps.last()
imageView.setImageBitmap(
- currentFrameBitmap
+ currentFrameBitmap,
)
return this
}
override fun getViewBitmap(): Bitmap = currentFrameBitmap
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SlideRightToLeftEffect.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SlideRightToLeftEffect.kt
index 960fbb65..e3011ac4 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SlideRightToLeftEffect.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/ui/effects/SlideRightToLeftEffect.kt
@@ -10,7 +10,7 @@ import com.tejpratapsingh.motionlib.core.animation.MotionInterpolator
class SlideRightToLeftEffect(
override val motionView: MotionView,
override val startFrame: Int,
- override val endFrame: Int
+ override val endFrame: Int,
) : MotionEffect {
private val TAG by lazy {
"SlideRightToLeftEffect"
@@ -22,12 +22,13 @@ class SlideRightToLeftEffect(
val view = motionView as View
- val progress = MotionInterpolator.interpolateForRange(
- interpolator = Interpolators(Easings.LINEAR),
- currentFrame = frame,
- frameRange = Pair(startFrame, endFrame),
- valueRange = Pair(0f, 1f)
- )
+ val progress =
+ MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(0f, 1f),
+ )
val width = view.width.toFloat()
@@ -36,4 +37,4 @@ class SlideRightToLeftEffect(
return motionView
}
-}
\ No newline at end of file
+}
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/TextViewUtil.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/TextViewUtil.kt
index fefa45f3..724df474 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/TextViewUtil.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/TextViewUtil.kt
@@ -10,8 +10,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.io.File
-fun TextView.getWebFont(url: String): Typeface? {
- return runBlocking(Dispatchers.IO) {
+fun TextView.getWebFont(url: String): Typeface? =
+ runBlocking(Dispatchers.IO) {
val client = HttpClient(CIO)
try {
val response = client.get(url).body()
@@ -25,4 +25,3 @@ fun TextView.getWebFont(url: String): Typeface? {
client.close()
}
}
-}
\ No newline at end of file
diff --git a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/VideoUtil.kt b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/VideoUtil.kt
index 72fd6350..b158f866 100644
--- a/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/VideoUtil.kt
+++ b/modules/motionlib/src/main/java/com/tejpratapsingh/motionlib/utils/VideoUtil.kt
@@ -17,7 +17,7 @@ import android.net.Uri
fun extractAllVideoFrames(
context: Context,
videoUri: Uri,
- frameIntervalUs: Long
+ frameIntervalUs: Long,
): List {
val retriever = MediaMetadataRetriever()
val frames = mutableListOf()
@@ -27,18 +27,22 @@ fun extractAllVideoFrames(
retriever.setDataSource(context, videoUri)
// 2. Retrieve video duration (in microseconds)
- val durationUs = retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_DURATION
- )?.toLongOrNull()?.times(1000) ?: 0L
+ val durationUs =
+ retriever
+ .extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_DURATION,
+ )?.toLongOrNull()
+ ?.times(1000) ?: 0L
// 3. Iterate through timestamps from 0 to duration, stepping by frameIntervalUs
var timeUs = 0L
while (timeUs < durationUs) {
// OPTIONALLY: Use OPTION_CLOSEST or OPTION_CLOSEST_SYNC
- val bitmap = retriever.getFrameAtTime(
- timeUs,
- MediaMetadataRetriever.OPTION_CLOSEST
- )
+ val bitmap =
+ retriever.getFrameAtTime(
+ timeUs,
+ MediaMetadataRetriever.OPTION_CLOSEST,
+ )
bitmap?.let { frames.add(it) }
timeUs += frameIntervalUs
}
@@ -56,14 +60,18 @@ fun extractAllVideoFrames(
* @param videoUri Uri of the video (content://, file://, etc.).
* @return FPS as a Float, or null if unavailable.
*/
-fun getVideoFpsWithRetriever(context: Context, videoUri: Uri): Float? {
+fun getVideoFpsWithRetriever(
+ context: Context,
+ videoUri: Uri,
+): Float? {
val retriever = MediaMetadataRetriever()
return try {
retriever.setDataSource(context, videoUri)
// METADATA_KEY_CAPTURE_FRAMERATE introduced in API 24
- retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE
- )?.toFloatOrNull()
+ retriever
+ .extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE,
+ )?.toFloatOrNull()
} finally {
retriever.release()
}
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 dd3e5a76..8e7ca3cc 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
@@ -14,9 +14,8 @@ import java.io.File
abstract class MotionWorker(
appContext: Context,
- workerParams: WorkerParameters
+ workerParams: WorkerParameters,
) : CoroutineWorker(appContext, workerParams) {
-
companion object {
private const val TAG = "MotionWorker"
const val PROGRESS_KEY = "progress"
@@ -38,32 +37,35 @@ abstract class MotionWorker(
override suspend fun doWork(): Result {
Log.d(TAG, "Worker ${this.id}: Starting video generation.")
return try {
- val videoFile: File = generateVideo(
- motionVideoProducer = mMotionVideoProducer,
- progressListener = { progress, currentBitmap ->
- // Report progress to WorkManager
- val progressData = workDataOf(
- PROGRESS_KEY to progress,
- TOTAL_FRAMES_KEY to mMotionVideoProducer.totalFrames
- )
- setProgressAsync(progressData)
+ val videoFile: File =
+ generateVideo(
+ motionVideoProducer = mMotionVideoProducer,
+ progressListener = { progress, currentBitmap ->
+ // Report progress to WorkManager
+ val progressData =
+ workDataOf(
+ PROGRESS_KEY to progress,
+ TOTAL_FRAMES_KEY to mMotionVideoProducer.totalFrames,
+ )
+ setProgressAsync(progressData)
- // Call the abstract onProgress for more specific handling
- onProgress(
- totalFrames = mMotionVideoProducer.totalFrames,
- currentProgress = progress, // Renamed for clarity
- bitmap = currentBitmap
- )
- }
- )
+ // Call the abstract onProgress for more specific handling
+ onProgress(
+ totalFrames = mMotionVideoProducer.totalFrames,
+ currentProgress = progress, // Renamed for clarity
+ bitmap = currentBitmap,
+ )
+ },
+ )
this.onCompleted(videoFile = videoFile)
Log.d(
TAG,
- "Worker ${this.workId}: Video generation successful: ${videoFile.absolutePath}"
- )
- val outputData = workDataOf(
- KEY_OUTPUT_VIDEO_URI to videoFile.toUri().toString()
+ "Worker ${this.workId}: Video generation successful: ${videoFile.absolutePath}",
)
+ val outputData =
+ workDataOf(
+ KEY_OUTPUT_VIDEO_URI to videoFile.toUri().toString(),
+ )
Result.success(outputData)
} catch (e: Exception) {
Log.e(TAG, "Worker ${this.workId}: Error during video generation.", e)
@@ -86,7 +88,11 @@ abstract class MotionWorker(
* @param currentProgress The number of frames processed so far.
* @param bitmap The current frame/bitmap being processed.
*/
- abstract fun onProgress(totalFrames: Int, currentProgress: Int, bitmap: Bitmap)
+ abstract fun onProgress(
+ totalFrames: Int,
+ currentProgress: Int,
+ bitmap: Bitmap,
+ )
/**
* Called when the video generation is completed successfully.
@@ -108,24 +114,25 @@ abstract class MotionWorker(
private suspend fun generateVideo(
motionVideoProducer: MotionVideoProducer,
- progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?
+ progressListener: ((progress: Int, bitmap: Bitmap) -> Unit)?,
): File =
withContext(Dispatchers.IO) {
- val outputFile = File.createTempFile(
- "motion_video_out_", // More descriptive prefix
- ".mp4",
- applicationContext.cacheDir // Use cacheDir for temp files that can be cleared
- )
+ val outputFile =
+ File.createTempFile(
+ "motion_video_out_", // More descriptive prefix
+ ".mp4",
+ applicationContext.cacheDir, // Use cacheDir for temp files that can be cleared
+ )
Log.d(
TAG,
- "Worker ${this@MotionWorker.workId}: Generating video at ${outputFile.absolutePath}"
+ "Worker ${this@MotionWorker.workId}: Generating video at ${outputFile.absolutePath}",
)
// Assuming produceVideo handles its own exceptions or lets them propagate
return@withContext motionVideoProducer.produceVideo(
context = applicationContext,
outputFile = outputFile,
- progressListener = progressListener
+ progressListener = progressListener,
)
}
}
diff --git a/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/ExampleUnitTest.kt b/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/ExampleUnitTest.kt
index 4fddccf9..c5a1a975 100644
--- a/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/ExampleUnitTest.kt
+++ b/modules/motionlib/src/test/java/com/tejpratapsingh/motionlib/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/PyTorchImageProcessor.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/PyTorchImageProcessor.kt
index 6694909a..e6daaf49 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/PyTorchImageProcessor.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/PyTorchImageProcessor.kt
@@ -16,10 +16,9 @@ object PyTorchImageProcessor {
*/
val backgroundRemoverPlugin: MotionPlugin by lazy {
object : MotionPlugin {
- override fun apply(input: Bitmap): Bitmap {
- return backgroundRemover.clearBackground(input)
+ override fun apply(input: Bitmap): Bitmap =
+ backgroundRemover.clearBackground(input)
?: throw IllegalStateException("Super Resolution processing failed")
- }
}
}
@@ -29,10 +28,9 @@ object PyTorchImageProcessor {
*/
val superResolutionPlugin: MotionPlugin by lazy {
object : MotionPlugin {
- override fun apply(input: Bitmap): Bitmap {
- return superResolutionProcessor.upscaleImage(input)
+ override fun apply(input: Bitmap): Bitmap =
+ superResolutionProcessor.upscaleImage(input)
?: throw IllegalStateException("Super Resolution processing failed")
- }
}
}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/common/ModelTypes.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/common/ModelTypes.kt
index 2d80d3c2..310585e8 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/common/ModelTypes.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/common/ModelTypes.kt
@@ -1,6 +1,8 @@
package com.tejpratapsingh.motionlib.pytorch.common
-enum class ModelTypes(val fileName: String) {
+enum class ModelTypes(
+ val fileName: String,
+) {
U2NET("u2net.ptl"),
- NINASR("ninasr_b0_2x.ptl")
-}
\ No newline at end of file
+ NINASR("ninasr_b0_2x.ptl"),
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/RemoveBg.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/RemoveBg.kt
index 6706f289..0c2dae74 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/RemoveBg.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/RemoveBg.kt
@@ -16,14 +16,16 @@ import org.pytorch.LiteModuleLoader
import org.pytorch.Module
import org.pytorch.torchvision.TensorImageUtils
-class RemoveBg(context: Context) : Remover {
-
- private var module: Module = LiteModuleLoader.load(
- assetFilePath(
- context,
- ModelTypes.U2NET.fileName
+class RemoveBg(
+ context: Context,
+) : Remover {
+ private var module: Module =
+ LiteModuleLoader.load(
+ assetFilePath(
+ context,
+ ModelTypes.U2NET.fileName,
+ ),
)
- )
private val maskPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val size = 320
@@ -38,7 +40,10 @@ class RemoveBg(context: Context) : Remover {
return removeBackground(mutableImage)
}
- override fun getMaskedImage(input: Bitmap, mask: Bitmap): Bitmap {
+ override fun getMaskedImage(
+ input: Bitmap,
+ mask: Bitmap,
+ ): Bitmap {
val result = createBitmap(mask.width, mask.height)
val mCanvas = Canvas(result)
@@ -52,15 +57,15 @@ class RemoveBg(context: Context) : Remover {
val height = input.height
val scaledBitmap = input.scale(size, size)
- val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
- scaledBitmap,
- TensorImageUtils.TORCHVISION_NORM_MEAN_RGB,
- TensorImageUtils.TORCHVISION_NORM_STD_RGB
- )
+ val inputTensor =
+ TensorImageUtils.bitmapToFloat32Tensor(
+ scaledBitmap,
+ TensorImageUtils.TORCHVISION_NORM_MEAN_RGB,
+ TensorImageUtils.TORCHVISION_NORM_STD_RGB,
+ )
val outputTensor = module.forward(IValue.from(inputTensor)).toTuple()
val arr = outputTensor[0].toTensor().dataAsFloatArray
val scaledMask = NetUtils.convertArrayToBitmap(arr, size, size)?.scale(width, height)
return scaledMask?.let { getMaskedImage(input, it) }
}
-
-}
\ No newline at end of file
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/Remover.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/Remover.kt
index 22d43f69..5a6bd210 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/Remover.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/removebg/Remover.kt
@@ -4,9 +4,10 @@ package com.tejpratapsingh.motionlib.pytorch.removebg
* Created by erenalpaslan on 11.09.2023
*/
interface Remover {
-
fun clearBackground(image: T): T?
- fun getMaskedImage(input: T, mask: T): T
-
-}
\ No newline at end of file
+ fun getMaskedImage(
+ input: T,
+ mask: T,
+ ): T
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/ImageUpscaler.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/ImageUpscaler.kt
index eb228984..eb19235d 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/ImageUpscaler.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/ImageUpscaler.kt
@@ -13,8 +13,9 @@ import java.io.File
import java.io.FileOutputStream
import java.io.IOException
-class ImageUpscaler(context: Context) {
-
+class ImageUpscaler(
+ context: Context,
+) {
private var module: Module
companion object {
@@ -29,13 +30,13 @@ class ImageUpscaler(context: Context) {
} catch (e: IOException) {
throw RuntimeException(
"Failed to load super resolution model: $MODEL_NAME. Ensure it's in app/src/main/assets/",
- e
+ e,
)
}
}
- fun upscaleImage(inputBitmap: Bitmap): Bitmap? {
- return try {
+ fun upscaleImage(inputBitmap: Bitmap): Bitmap? =
+ try {
val inputTensor = preprocessImage(inputBitmap)
val outputTensor = module.forward(IValue.from(inputTensor)).toTensor()
postprocessOutput(outputTensor)
@@ -43,7 +44,6 @@ class ImageUpscaler(context: Context) {
e.printStackTrace() // Consider using a proper logger
null
}
- }
private fun preprocessImage(bitmap: Bitmap): Tensor {
val resizedBitmap = bitmap.scale(INPUT_SIZE, INPUT_SIZE)
@@ -51,7 +51,7 @@ class ImageUpscaler(context: Context) {
return TensorImageUtils.bitmapToFloat32Tensor(
resizedBitmap,
floatArrayOf(0.485f, 0.456f, 0.406f), // Example: ImageNet mean
- floatArrayOf(0.229f, 0.224f, 0.225f) // Example: ImageNet std
+ floatArrayOf(0.229f, 0.224f, 0.225f), // Example: ImageNet std
)
}
@@ -100,7 +100,10 @@ class ImageUpscaler(context: Context) {
}
@Throws(IOException::class)
- private fun assetFilePath(context: Context, assetName: String): String {
+ private fun assetFilePath(
+ context: Context,
+ assetName: String,
+ ): String {
val assetFile = File(context.filesDir, assetName)
if (assetFile.exists() && assetFile.length() > 0) {
return assetFile.absolutePath
@@ -118,4 +121,4 @@ class ImageUpscaler(context: Context) {
}
return assetFile.absolutePath
}
-}
\ No newline at end of file
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/SuperRes.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/SuperRes.kt
index 1926292d..19280058 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/SuperRes.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/superres/SuperRes.kt
@@ -13,8 +13,9 @@ import java.io.File
import java.io.FileOutputStream
import java.io.IOException
-class SuperRes(context: Context) {
-
+class SuperRes(
+ context: Context,
+) {
private var module: Module
companion object {
@@ -31,8 +32,8 @@ class SuperRes(context: Context) {
}
}
- fun upscaleImage(inputBitmap: Bitmap): Bitmap? {
- return try {
+ fun upscaleImage(inputBitmap: Bitmap): Bitmap? =
+ try {
// Preprocess the input image
val inputTensor = preprocessImage(inputBitmap)
@@ -45,7 +46,6 @@ class SuperRes(context: Context) {
e.printStackTrace()
null
}
- }
private fun preprocessImage(bitmap: Bitmap): Tensor {
// Resize image to model input size
@@ -55,11 +55,14 @@ class SuperRes(context: Context) {
return TensorImageUtils.bitmapToFloat32Tensor(
resizedBitmap,
floatArrayOf(0.485f, 0.456f, 0.406f), // ImageNet mean
- floatArrayOf(0.229f, 0.224f, 0.225f) // ImageNet std
+ floatArrayOf(0.229f, 0.224f, 0.225f), // ImageNet std
)
}
- private fun postprocessOutput(outputTensor: Tensor, originalBitmap: Bitmap): Bitmap {
+ private fun postprocessOutput(
+ outputTensor: Tensor,
+ originalBitmap: Bitmap,
+ ): Bitmap {
// Get tensor data
val outputData = outputTensor.dataAsFloatArray
val shape = outputTensor.shape()
@@ -88,8 +91,10 @@ class SuperRes(context: Context) {
// Assumes CHW planar format: RRR...GGG...BBB...
r = (outputData[baseIndex] * 255).coerceIn(0f, 255f).toInt()
g = (outputData[height * width + baseIndex] * 255).coerceIn(0f, 255f).toInt()
- b = (outputData[2 * height * width + baseIndex] * 255).coerceIn(0f, 255f)
- .toInt()
+ b =
+ (outputData[2 * height * width + baseIndex] * 255)
+ .coerceIn(0f, 255f)
+ .toInt()
} else {
throw IllegalArgumentException("Unsupported number of output channels: $channels. Expected 1 or 3.")
}
@@ -102,7 +107,10 @@ class SuperRes(context: Context) {
}
@Throws(IOException::class)
- private fun assetFilePath(context: Context, assetName: String): String {
+ private fun assetFilePath(
+ context: Context,
+ assetName: String,
+ ): String {
val file = File(context.filesDir, assetName)
if (file.exists() && file.length() > 0) {
return file.absolutePath
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/FileUtils.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/FileUtils.kt
index 884a0095..c5e90bd0 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/FileUtils.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/FileUtils.kt
@@ -7,7 +7,10 @@ import java.io.IOException
object FileUtils {
@Throws(IOException::class)
- fun assetFilePath(context: Context, assetName: String): String? {
+ fun assetFilePath(
+ context: Context,
+ assetName: String,
+ ): String? {
val file = File(context.filesDir, assetName)
if (file.exists() && file.length() > 0) {
return file.absolutePath
@@ -24,4 +27,4 @@ object FileUtils {
return file.absolutePath
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/NetUtils.kt b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/NetUtils.kt
index fac63954..8652288a 100644
--- a/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/NetUtils.kt
+++ b/modules/pytorch-motion-ext/src/main/java/com/tejpratapsingh/motionlib/pytorch/utils/NetUtils.kt
@@ -5,7 +5,11 @@ import androidx.core.graphics.createBitmap
import androidx.core.graphics.set
object NetUtils {
- fun convertArrayToBitmap(arr: FloatArray, width: Int, height: Int): Bitmap? {
+ fun convertArrayToBitmap(
+ arr: FloatArray,
+ width: Int,
+ height: Int,
+ ): Bitmap? {
val grayToneImage = createBitmap(width, height)
for (i in 0 until width) {
for (j in 0 until height) {
@@ -14,4 +18,4 @@ object NetUtils {
}
return grayToneImage
}
-}
\ No newline at end of file
+}
diff --git a/modules/pytorch-motion-ext/src/test/java/com/tejpratapsingh/motionlib/pytorch/ExampleUnitTest.kt b/modules/pytorch-motion-ext/src/test/java/com/tejpratapsingh/motionlib/pytorch/ExampleUnitTest.kt
index fd13f8a5..a03cb9ef 100644
--- a/modules/pytorch-motion-ext/src/test/java/com/tejpratapsingh/motionlib/pytorch/ExampleUnitTest.kt
+++ b/modules/pytorch-motion-ext/src/test/java/com/tejpratapsingh/motionlib/pytorch/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/sdui/src/androidTest/java/com/tejpratapsingh/motion/sdui/ExampleInstrumentedTest.kt b/modules/sdui/src/androidTest/java/com/tejpratapsingh/motion/sdui/ExampleInstrumentedTest.kt
index b46b0c19..f7a82249 100644
--- a/modules/sdui/src/androidTest/java/com/tejpratapsingh/motion/sdui/ExampleInstrumentedTest.kt
+++ b/modules/sdui/src/androidTest/java/com/tejpratapsingh/motion/sdui/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package com.tejpratapsingh.motion.sdui
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.tejpratapsingh.motion.sdui.test", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/data/SduiRenderer.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/data/SduiRenderer.kt
index edc78ec0..ffaa1753 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/data/SduiRenderer.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/data/SduiRenderer.kt
@@ -15,7 +15,7 @@ import com.tejpratapsingh.motion.sdui.presentation.TextFactory
class SduiRenderer(
val actionHandler: ActionHandler = DefaultActionHandler(),
- val imageLoader: ImageLoader? = null
+ val imageLoader: ImageLoader? = null,
) {
private val gson = Gson()
private val factories: MutableMap = mutableMapOf()
@@ -26,19 +26,28 @@ class SduiRenderer(
register("image", ImageFactory())
}
- fun register(type: String, factory: ViewFactory) {
+ fun register(
+ type: String,
+ factory: ViewFactory,
+ ) {
factories[type] = factory
}
- fun createView(context: Context, json: JsonObject): View {
+ fun createView(
+ context: Context,
+ json: JsonObject,
+ ): View {
val type = json.get("type")?.asString ?: error("Missing type")
val factory = factories[type] ?: throw IllegalArgumentException("No factory for type $type")
return factory.create(context, json, this)
}
- fun renderInto(container: ViewGroup, json: String) {
+ fun renderInto(
+ container: ViewGroup,
+ json: String,
+ ) {
val rootJson = gson.fromJson(json, JsonObject::class.java)
val view = createView(container.context, rootJson)
container.addView(view)
}
-}
\ No newline at end of file
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ActionHandler.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ActionHandler.kt
index 801d33c9..e9069b94 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ActionHandler.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ActionHandler.kt
@@ -4,5 +4,8 @@ import android.content.Context
import com.google.gson.JsonObject
fun interface ActionHandler {
- fun handle(context: Context, action: JsonObject)
-}
\ No newline at end of file
+ fun handle(
+ context: Context,
+ action: JsonObject,
+ )
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ImageLoader.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ImageLoader.kt
index c4b72468..b86d4ee8 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ImageLoader.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ImageLoader.kt
@@ -3,5 +3,8 @@ package com.tejpratapsingh.motion.sdui.domain
import android.widget.ImageView
fun interface ImageLoader {
- fun load(view: ImageView, url: String)
-}
\ No newline at end of file
+ fun load(
+ view: ImageView,
+ url: String,
+ )
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ViewFactory.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ViewFactory.kt
index 345fa0dc..33d19d63 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ViewFactory.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/domain/ViewFactory.kt
@@ -6,5 +6,9 @@ import com.google.gson.JsonObject
import com.tejpratapsingh.motion.sdui.data.SduiRenderer
fun interface ViewFactory {
- fun create(context: Context, json: JsonObject, renderer: SduiRenderer): View
-}
\ No newline at end of file
+ fun create(
+ context: Context,
+ json: JsonObject,
+ renderer: SduiRenderer,
+ ): View
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ContainerFactory.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ContainerFactory.kt
index 3f5444dd..2c8045ab 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ContainerFactory.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ContainerFactory.kt
@@ -9,10 +9,15 @@ import com.tejpratapsingh.motion.sdui.data.SduiRenderer
import com.tejpratapsingh.motion.sdui.domain.ViewFactory
class ContainerFactory : ViewFactory {
- override fun create(context: Context, json: JsonObject, renderer: SduiRenderer): View {
- val layout = LinearLayout(context).apply {
- orientation = if (json.get("orientation")?.asString == "horizontal") LinearLayout.HORIZONTAL else LinearLayout.VERTICAL
- }
+ override fun create(
+ context: Context,
+ json: JsonObject,
+ renderer: SduiRenderer,
+ ): View {
+ val layout =
+ LinearLayout(context).apply {
+ orientation = if (json.get("orientation")?.asString == "horizontal") LinearLayout.HORIZONTAL else LinearLayout.VERTICAL
+ }
val children = json.getAsJsonArray("children") ?: JsonArray()
for (i in 0 until children.size()) {
val childJson = children.get(i).asJsonObject
@@ -21,4 +26,4 @@ class ContainerFactory : ViewFactory {
}
return layout
}
-}
\ No newline at end of file
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/DefaultActionHandler.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/DefaultActionHandler.kt
index 08b3ab22..ecdeb087 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/DefaultActionHandler.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/DefaultActionHandler.kt
@@ -6,11 +6,14 @@ import com.google.gson.JsonObject
import com.tejpratapsingh.motion.sdui.domain.ActionHandler
class DefaultActionHandler : ActionHandler {
- override fun handle(context: Context, action: JsonObject) {
+ override fun handle(
+ context: Context,
+ action: JsonObject,
+ ) {
val type = action.get("type")?.asString
when (type) {
"toast" -> Toast.makeText(context, action.get("message")?.asString ?: "", Toast.LENGTH_SHORT).show()
else -> Toast.makeText(context, "Unknown action: $type", Toast.LENGTH_SHORT).show()
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ImageFactory.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ImageFactory.kt
index 1e85edc7..8d185f1c 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ImageFactory.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/ImageFactory.kt
@@ -8,7 +8,11 @@ import com.tejpratapsingh.motion.sdui.data.SduiRenderer
import com.tejpratapsingh.motion.sdui.domain.ViewFactory
class ImageFactory : ViewFactory {
- override fun create(context: Context, json: JsonObject, renderer: SduiRenderer): View {
+ override fun create(
+ context: Context,
+ json: JsonObject,
+ renderer: SduiRenderer,
+ ): View {
val imageView = ImageView(context)
renderer.imageLoader?.load(imageView, json.get("url")?.asString ?: "")
return imageView
diff --git a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/TextFactory.kt b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/TextFactory.kt
index 58e7adb6..a603def6 100644
--- a/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/TextFactory.kt
+++ b/modules/sdui/src/main/java/com/tejpratapsingh/motion/sdui/presentation/TextFactory.kt
@@ -8,10 +8,13 @@ import com.tejpratapsingh.motion.sdui.data.SduiRenderer
import com.tejpratapsingh.motion.sdui.domain.ViewFactory
class TextFactory : ViewFactory {
- override fun create(context: Context, json: JsonObject, renderer: SduiRenderer): View {
- return TextView(context).apply {
+ override fun create(
+ context: Context,
+ json: JsonObject,
+ renderer: SduiRenderer,
+ ): View =
+ TextView(context).apply {
text = json.get("text")?.asString ?: ""
textSize = json.get("textSize")?.asFloat ?: 16f
}
- }
}
diff --git a/modules/sdui/src/test/java/com/tejpratapsingh/motion/sdui/ExampleUnitTest.kt b/modules/sdui/src/test/java/com/tejpratapsingh/motion/sdui/ExampleUnitTest.kt
index be5d5295..7c830f9e 100644
--- a/modules/sdui/src/test/java/com/tejpratapsingh/motion/sdui/ExampleUnitTest.kt
+++ b/modules/sdui/src/test/java/com/tejpratapsingh/motion/sdui/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package com.tejpratapsingh.motion.sdui
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
@@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/ExampleUnitTest.kt b/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/ExampleUnitTest.kt
index 35e40556..bc6484e5 100644
--- a/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/ExampleUnitTest.kt
+++ b/modules/templates/src/test/java/com/tejpratapsingh/motionlib/templates/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/ImageUtils.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/ImageUtils.kt
index ff0c220c..4d7f5c6b 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/ImageUtils.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/ImageUtils.kt
@@ -40,7 +40,10 @@ object ImageUtils {
return bitmap
}
- fun applyMask(original: Bitmap, mask: Array): Bitmap {
+ fun applyMask(
+ original: Bitmap,
+ mask: Array,
+ ): Bitmap {
val width = original.width
val height = original.height
val scaledMask = floatArrayToGrayscaleBitmap(mask).scale(width, height)
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/TensorFlowImageProcessor.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/TensorFlowImageProcessor.kt
index a0c531ef..4127b4f1 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/TensorFlowImageProcessor.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/TensorFlowImageProcessor.kt
@@ -16,9 +16,7 @@ object TensorFlowImageProcessor {
*/
val superResolutionPlugin: MotionPlugin by lazy {
object : MotionPlugin {
- override fun apply(input: Bitmap): Bitmap {
- return superResolutionProcessor.enhance(input)
- }
+ override fun apply(input: Bitmap): Bitmap = superResolutionProcessor.enhance(input)
}
}
@@ -28,9 +26,7 @@ object TensorFlowImageProcessor {
*/
val backgroundRemovalPlugin: MotionPlugin by lazy {
object : MotionPlugin {
- override fun apply(input: Bitmap): Bitmap {
- return backgroundRemover.removeBackground(input)
- }
+ override fun apply(input: Bitmap): Bitmap = backgroundRemover.removeBackground(input)
}
}
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/CarBgRemover.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/CarBgRemover.kt
index a63489e8..6b62c8e7 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/CarBgRemover.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/CarBgRemover.kt
@@ -16,11 +16,12 @@ import java.io.FileInputStream
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
-class CarBgRemover(context: Context) {
-
+class CarBgRemover(
+ context: Context,
+) {
companion object {
private const val MODEL_FILENAME = "DeepLabV3-Plus-MobileNet.tflite"
- private const val PERSON_CLASS_INDEX = 15 // PASCAL VOC “person” index
+ private const val PERSON_CLASS_INDEX = 15 // PASCAL VOC “person” index
private const val FOREGROUND_THRESHOLD = 0.5f
}
@@ -29,7 +30,10 @@ class CarBgRemover(context: Context) {
Interpreter(loadModelFile(context, MODEL_FILENAME))
}
- private fun loadModelFile(context: Context, modelName: String): MappedByteBuffer {
+ private fun loadModelFile(
+ context: Context,
+ modelName: String,
+ ): MappedByteBuffer {
val fileDescriptor = context.assets.openFd(modelName)
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
@@ -47,7 +51,8 @@ class CarBgRemover(context: Context) {
// Preprocessor: resize + normalize to [-1,1]
private val preprocessor: ImageProcessor by lazy {
- ImageProcessor.Builder()
+ ImageProcessor
+ .Builder()
.add(ResizeOp(inputHeight, inputWidth, ResizeOp.ResizeMethod.BILINEAR))
.add(NormalizeOp(127.5f, 127.5f))
.build()
@@ -64,7 +69,7 @@ class CarBgRemover(context: Context) {
// 2. Prepare output buffer
val outputTensor = interpreter.getOutputTensor(0)
- val outputShape = outputTensor.shape() // [1, H_out, W_out, C]
+ val outputShape = outputTensor.shape() // [1, H_out, W_out, C]
val outputDataType = outputTensor.dataType()
val outputBuffer = TensorBuffer.createFixedSize(outputShape, outputDataType)
@@ -72,11 +77,11 @@ class CarBgRemover(context: Context) {
interpreter.run(inputBuffer, outputBuffer.buffer.rewind())
// 4. Extract “person” mask channel
- val logits = outputBuffer.floatArray // length = H_out * W_out * C
+ val logits = outputBuffer.floatArray // length = H_out * W_out * C
val maskWidth = outputShape[2]
val maskHeight = outputShape[1]
val mask = FloatArray(maskWidth * maskHeight)
- val stride = outputShape[3] // number of classes
+ val stride = outputShape[3] // number of classes
for (i in mask.indices) {
mask[i] = logits[i * stride + PERSON_CLASS_INDEX]
@@ -90,7 +95,7 @@ class CarBgRemover(context: Context) {
source: Bitmap,
mask: FloatArray,
maskWidth: Int,
- maskHeight: Int
+ maskHeight: Int,
): Bitmap {
val w = source.width
val h = source.height
@@ -102,8 +107,12 @@ class CarBgRemover(context: Context) {
val my = y * maskHeight / h
val idx = my * maskWidth + mx
val pixel = source[x, y]
- result[x, y] = if (mask[idx] > FOREGROUND_THRESHOLD) pixel
- else Color.TRANSPARENT
+ result[x, y] =
+ if (mask[idx] > FOREGROUND_THRESHOLD) {
+ pixel
+ } else {
+ Color.TRANSPARENT
+ }
}
}
return result
@@ -113,4 +122,4 @@ class CarBgRemover(context: Context) {
fun close() {
interpreter.close()
}
-}
\ No newline at end of file
+}
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TfLiteSegmentationHelper.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TfLiteSegmentationHelper.kt
index d08e7842..e1f74fb8 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TfLiteSegmentationHelper.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TfLiteSegmentationHelper.kt
@@ -16,7 +16,9 @@ import org.tensorflow.lite.support.image.ImageProcessor
import org.tensorflow.lite.support.image.TensorImage
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer
-class TfLiteSegmentationHelper(context: Context) {
+class TfLiteSegmentationHelper(
+ context: Context,
+) {
private val modelName = "deeplabv3_257_mv_gpu.tflite"
private val inputSize = 257
private val interpreter: Interpreter
@@ -26,9 +28,7 @@ class TfLiteSegmentationHelper(context: Context) {
interpreter = Interpreter(modelFile)
}
- fun segmentAndRemoveBackground(
- bitmap: Bitmap
- ): Bitmap {
+ fun segmentAndRemoveBackground(bitmap: Bitmap): Bitmap {
// Run segmentation on background thread
val mask = runDeepLabSegmentation(bitmap)
return applyMask(bitmap, mask)
@@ -40,16 +40,19 @@ class TfLiteSegmentationHelper(context: Context) {
// 2. Prepare input tensor
val tensorImage = TensorImage.fromBitmap(resized)
- val processor = ImageProcessor.Builder()
- .add(NormalizeOp(127.5f, 127.5f))
- .build()
+ val processor =
+ ImageProcessor
+ .Builder()
+ .add(NormalizeOp(127.5f, 127.5f))
+ .build()
val input = processor.process(tensorImage)
// 3. Prepare output buffer
- val outputBuffer = TensorBuffer.createFixedSize(
- intArrayOf(1, inputSize, inputSize, 21),
- org.tensorflow.lite.DataType.FLOAT32
- )
+ val outputBuffer =
+ TensorBuffer.createFixedSize(
+ intArrayOf(1, inputSize, inputSize, 21),
+ org.tensorflow.lite.DataType.FLOAT32,
+ )
// 4. Run inference
interpreter.run(input.buffer, outputBuffer.buffer.rewind())
@@ -60,14 +63,21 @@ class TfLiteSegmentationHelper(context: Context) {
for (i in maskPixels.indices) {
val offset = i * 21
val maxIndex =
- scores.copyOfRange(offset, offset + 21).withIndex().maxByOrNull { it.value }!!.index
+ scores
+ .copyOfRange(offset, offset + 21)
+ .withIndex()
+ .maxByOrNull { it.value }!!
+ .index
maskPixels[i] = if (maxIndex == 15) Color.WHITE else Color.TRANSPARENT
}
val mask = Bitmap.createBitmap(maskPixels, inputSize, inputSize, Bitmap.Config.ARGB_8888)
return mask.scale(originalBitmap.width, originalBitmap.height)
}
- private fun applyMask(original: Bitmap, mask: Bitmap): Bitmap {
+ private fun applyMask(
+ original: Bitmap,
+ mask: Bitmap,
+ ): Bitmap {
val result = createBitmap(original.width, original.height)
val canvas = Canvas(result)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
@@ -77,4 +87,4 @@ class TfLiteSegmentationHelper(context: Context) {
paint.xfermode = null
return result
}
-}
\ No newline at end of file
+}
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TiledBackgroundRemover.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TiledBackgroundRemover.kt
index da403d29..a3b7e506 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TiledBackgroundRemover.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/removebg/TiledBackgroundRemover.kt
@@ -19,7 +19,7 @@ class TiledBackgroundRemover(
context: Context,
modelPath: String,
private val tileSize: Int = 257,
- private val overlap: Int = 64
+ private val overlap: Int = 64,
) {
private val interpreter: Interpreter
@@ -110,7 +110,10 @@ class TiledBackgroundRemover(
return ImageUtils.intArrayToGrayscaleBitmap(mask)
}
- private fun applyMaskToBitmap(src: Bitmap, mask: Bitmap): Bitmap {
+ private fun applyMaskToBitmap(
+ src: Bitmap,
+ mask: Bitmap,
+ ): Bitmap {
val w = src.width
val h = src.height
val result = createBitmap(w, h)
diff --git a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/superres/SuperResolutionProcessor.kt b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/superres/SuperResolutionProcessor.kt
index a9bee014..1d9ae4d0 100644
--- a/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/superres/SuperResolutionProcessor.kt
+++ b/modules/tensorflow-motion-ext/src/main/java/com/tejpratapsingh/motionlib/tensorflow/superres/SuperResolutionProcessor.kt
@@ -8,7 +8,7 @@ import org.tensorflow.lite.support.common.FileUtil
class SuperResolutionProcessor(
context: Context,
- modelFile: String
+ modelFile: String,
) {
private val interpreter: Interpreter
@@ -29,13 +29,14 @@ class SuperResolutionProcessor(
val outputHeight = inputHeight * 2
// Create output buffer shape: [1, height*2, width*2, 3]
- val outputBuffer = Array(1) {
- Array(outputHeight) {
- Array(outputWidth) {
- FloatArray(3)
+ val outputBuffer =
+ Array(1) {
+ Array(outputHeight) {
+ Array(outputWidth) {
+ FloatArray(3)
+ }
}
}
- }
// Resize input tensor before running, if model allows dynamic input
interpreter.resizeInput(0, intArrayOf(1, inputHeight, inputWidth, 3))
@@ -47,4 +48,4 @@ class SuperResolutionProcessor(
// Convert output float array to Bitmap
return ImageUtils.floatArrayToBitmap(outputBuffer[0])
}
-}
\ No newline at end of file
+}
diff --git a/modules/tensorflow-motion-ext/src/test/java/com/tejpratapsingh/motionlib/tensorflow/ExampleUnitTest.kt b/modules/tensorflow-motion-ext/src/test/java/com/tejpratapsingh/motionlib/tensorflow/ExampleUnitTest.kt
index e36e2fd2..3156ed84 100644
--- a/modules/tensorflow-motion-ext/src/test/java/com/tejpratapsingh/motionlib/tensorflow/ExampleUnitTest.kt
+++ b/modules/tensorflow-motion-ext/src/test/java/com/tejpratapsingh/motionlib/tensorflow/ExampleUnitTest.kt
@@ -13,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-}
\ No newline at end of file
+}