diff --git a/README.md b/README.md
index 9958ee8..84f3306 100644
--- a/README.md
+++ b/README.md
@@ -62,27 +62,130 @@ FFmpeg Android runs on the following architectures:
### Dependency
- [MobileFFmpeg](https://github.com/tanersener/mobile-ffmpeg)
-### Gradle Dependency
-* Add it in your root build.gradle at the end of repositories:
+**Note:** This library includes the mobile-ffmpeg.aar file (full version) in the `SSffmpegVideoOperation/libs/` directory. The full version includes all FFmpeg features and codecs needed for comprehensive video operations.
- ```
+**For JitPack users:** The mobile-ffmpeg dependency is automatically included when you use the JitPack dependency. No additional setup required!
+
+### Integration Guide
+
+This library uses the mobile-ffmpeg AAR dependency which is included in the library's libs folder. Follow these simple steps to integrate the library into your project.
+
+#### Step 1: Download the Library
+* Download or clone this repository
+* Copy the `SSffmpegVideoOperation` module folder to your project
+
+#### Step 2: Integration Methods
+
+**Option A: Using JitPack (Recommended - Simple One-Line Integration)**
+
+This is the easiest way to integrate the library. JitPack will build the library along with the mobile-ffmpeg dependency automatically.
+
+* Add JitPack repository to your root build.gradle:
+
+ ```gradle
allprojects {
repositories {
- ...
+ google()
+ mavenCentral()
maven { url 'https://jitpack.io' }
}
}
```
-* Add the dependency in your app's build.gradle file
+* Add the dependency in your app's build.gradle file:
- ```
+ ```gradle
dependencies {
implementation 'com.github.SimformSolutionsPvtLtd:SSffmpegVideoOperation:1.0.8'
}
```
-This is all you have to do to load the FFmpeg library.
+**That's it!** JitPack will automatically handle the mobile-ffmpeg.aar dependency that's included in this repository. No additional configuration needed.
+
+**Option B: Local Integration (For Customization)**
+
+If you want to modify the library or integrate it locally, follow these steps:
+
+1. **Add the library module to your `settings.gradle`:**
+ ```gradle
+ include ':app', ':SSffmpegVideoOperation'
+ // If the SSffmpegVideoOperation folder is in a different location:
+ // project(':SSffmpegVideoOperation').projectDir = new File('path/to/SSffmpegVideoOperation')
+ ```
+
+2. **Configure repositories in your app's `build.gradle`:**
+ ```gradle
+ android {
+ // ... your existing configuration
+ }
+
+ repositories {
+ google()
+ mavenCentral()
+ flatDir {
+ dirs '../SSffmpegVideoOperation/libs'
+ }
+ }
+
+ dependencies {
+ implementation project(':SSffmpegVideoOperation')
+ // ... your other dependencies
+ }
+ ```
+
+3. **The SSffmpegVideoOperation module is already configured with:**
+ - `libs/mobile-ffmpeg.aar` - The full version of mobile-ffmpeg
+ - Proper repository configuration in its `build.gradle`:
+ ```gradle
+ repositories {
+ flatDir {
+ dirs 'libs'
+ }
+ }
+
+ dependencies {
+ implementation(name: 'mobile-ffmpeg', ext: 'aar')
+ // ... other dependencies
+ }
+ ```
+
+**Important Notes:**
+- The `mobile-ffmpeg.aar` file is already included in `SSffmpegVideoOperation/libs/`
+- No additional setup is required for the AAR dependency
+- The library uses `flatDir` repository to resolve the local AAR file
+- Make sure to sync your project after adding the module
+
+## How JitPack Integration Works
+
+When you use the JitPack dependency (`implementation 'com.github.SimformSolutionsPvtLtd:SSffmpegVideoOperation:1.0.8'`):
+
+1. **JitPack automatically builds** your library from the GitHub repository
+2. **Includes mobile-ffmpeg.aar** - The AAR file in `SSffmpegVideoOperation/libs/` is packaged with the library
+3. **Resolves dependencies** - All transitive dependencies are handled automatically
+4. **No local setup needed** - Users don't need to manually handle AAR files or repository configurations
+
+This approach gives you the best of both worlds:
+- **Simple integration** for users via JitPack
+- **Full control** over the mobile-ffmpeg dependency
+- **No external dependencies** on repositories that might change or become unavailable
+
+#### Step 3: Add Required Permissions
+Add these permissions to your AndroidManifest.xml:
+```xml
+
+
+
+
+```
+
+#### Step 4: ProGuard Configuration (if using ProGuard/R8)
+Add these rules to your proguard-rules.pro:
+```
+-keep class com.arthenica.mobileffmpeg.** { *; }
+-keep class com.simform.videooperations.** { *; }
+```
+
+This setup ensures proper AAR dependency resolution and avoids the "Direct local .aar file dependencies are not supported" error when building AAR libraries.
### Run FFmpeg command
In this sample code we will run the FFmpeg -version command in background call.
@@ -138,6 +241,64 @@ CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
same for other queries.
And you can apply your query also
+## Troubleshooting
+
+### Common Issues and Solutions
+
+#### 1. "Could not find mobile-ffmpeg.aar" Error
+**Solution:**
+- Ensure the `SSffmpegVideoOperation/libs/mobile-ffmpeg.aar` file exists
+- Check that your app's build.gradle has the correct flatDir repository configuration:
+ ```gradle
+ repositories {
+ flatDir {
+ dirs '../SSffmpegVideoOperation/libs'
+ }
+ }
+ ```
+
+#### 2. "INSTALL_PARSE_FAILED_NO_CERTIFICATES" Error
+**Solution:** Ensure your release builds are properly signed. Add signing configuration to your app's build.gradle:
+```gradle
+android {
+ signingConfigs {
+ debug {
+ storeFile file("${System.getProperty('user.home')}/.android/debug.keystore")
+ storePassword "android"
+ keyAlias "androiddebugkey"
+ keyPassword "android"
+ }
+ }
+ buildTypes {
+ release {
+ signingConfig signingConfigs.debug // Use debug signing for testing
+ // For production, create and use a proper release keystore
+ }
+ }
+}
+```
+
+#### 3. Build Sync Issues
+**Solution:**
+- Make sure the SSffmpegVideoOperation module is properly included in settings.gradle
+- Verify the module path is correct
+- Clean and rebuild the project
+
+#### 4. FFmpeg Commands Not Working
+**Solution:**
+- Check if you have the required permissions
+- Ensure input and output file paths are correct and accessible
+- Verify the FFmpeg command syntax
+
+#### 5. Memory Issues with Large Videos
+**Solution:**
+- Process videos in smaller chunks
+- Use appropriate compression settings
+- Consider using background processing for large files
+
+#### 6. "flatDir should be avoided" Warning
+**Solution:** This warning can be ignored. While flatDir is not the recommended approach for published libraries, it's acceptable for local AAR dependencies and works reliably for this use case.
+
## Medium Blog
For more info go to __[Multimedia Operations for Android using FFmpeg](https://medium.com/simform-engineering/multimedia-operations-for-android-using-ffmpeg-78f1fb480a83)__
diff --git a/SSffmpegVideoOperation/.gitignore b/SSffmpegVideoOperation/.gitignore
index f0df5ca..f406bdd 100644
--- a/SSffmpegVideoOperation/.gitignore
+++ b/SSffmpegVideoOperation/.gitignore
@@ -9,6 +9,7 @@
/captures
.externalNativeBuild
libs/
+!libs/mobile-ffmpeg.aar
.idea/gradle.xml
.idea/misc.xml
.idea/modules.xml
diff --git a/SSffmpegVideoOperation/build.gradle b/SSffmpegVideoOperation/build.gradle
index 23c767e..509a0fb 100644
--- a/SSffmpegVideoOperation/build.gradle
+++ b/SSffmpegVideoOperation/build.gradle
@@ -19,18 +19,19 @@ afterEvaluate {
}
repositories {
+ google()
+ mavenCentral()
flatDir {
dirs 'libs'
}
}
android {
- compileSdkVersion 30
- buildToolsVersion "29.0.3"
-
+ namespace 'com.simform.videooperations'
defaultConfig {
+ compileSdk 35
minSdkVersion 24
- targetSdkVersion 30
+ targetSdkVersion 35
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
@@ -69,11 +70,10 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- implementation 'androidx.core:core-ktx:1.8.0'
- implementation 'androidx.appcompat:appcompat:1.4.2'
- implementation 'com.google.android.material:material:1.6.1'
+ implementation 'androidx.core:core-ktx:1.9.0'
+ implementation 'androidx.appcompat:appcompat:1.7.1'
+ implementation 'com.google.android.material:material:1.12.0'
implementation 'pub.devrel:easypermissions:3.0.0'
- implementation(files("libs/mobile-ffmpeg.aar"))
+ implementation (name: 'mobile-ffmpeg', ext: 'aar')
implementation 'com.github.jaiselrahman:FilePicker:1.3.2'
}
-
diff --git a/SSffmpegVideoOperation/libs/mobile-ffmpeg.aar b/SSffmpegVideoOperation/libs/mobile-ffmpeg.aar
new file mode 100644
index 0000000..49bfb03
Binary files /dev/null and b/SSffmpegVideoOperation/libs/mobile-ffmpeg.aar differ
diff --git a/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Common.kt b/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Common.kt
index 4f9acc1..766cc71 100644
--- a/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Common.kt
+++ b/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Common.kt
@@ -1,15 +1,22 @@
package com.simform.videooperations
+import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.media.MediaExtractor
import android.media.MediaFormat
+import android.media.MediaMetadataRetriever
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.provider.OpenableColumns
import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import com.jaiselrahman.filepicker.activity.FilePickerActivity
import com.jaiselrahman.filepicker.config.Configurations
import java.io.File
import java.io.FileInputStream
+import java.io.FileOutputStream
import java.io.IOException
import java.text.DecimalFormat
import java.util.Formatter
@@ -162,7 +169,20 @@ object Common {
}
fun getFilePath(context: Context, fileExtension: String) : String {
- val dir = File(context.getExternalFilesDir(Common.OUT_PUT_DIR).toString())
+ val dir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ when {
+ TextUtils.equals(fileExtension, VIDEO) -> Environment.DIRECTORY_MOVIES
+ TextUtils.equals(fileExtension, IMAGE) || TextUtils.equals(fileExtension, GIF) -> Environment.DIRECTORY_PICTURES
+ TextUtils.equals(fileExtension, MP3) -> Environment.DIRECTORY_MUSIC
+ else -> Environment.DIRECTORY_DOWNLOADS
+ }.let {
+ File(Environment.getExternalStoragePublicDirectory(it).toString())
+ }
+ } else {
+ // Fallback to app's private external files dir on older Android versions
+ File(context.getExternalFilesDir(Common.OUT_PUT_DIR).toString())
+ }
+
if (!dir.exists()) {
dir.mkdirs()
}
@@ -181,7 +201,12 @@ object Common {
extension = ".mp3"
}
}
- val dest = File(dir.path + File.separator + Common.OUT_PUT_DIR + System.currentTimeMillis().div(1000L) + extension)
+ val dest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ File(dir, System.currentTimeMillis().div(1000L).toString() + extension)
+ } else {
+ // Fallback for devices below Android 14
+ File(dir.path + File.separator + OUT_PUT_DIR + System.currentTimeMillis().div(1000L) + extension)
+ }
return dest.absolutePath
}
@@ -196,4 +221,47 @@ object Common {
}
}
}
+
+ fun saveFileToTempAndGetPath(context: Context, uri: Uri): String? {
+ val contentResolver = context.contentResolver
+ val fileName = getFileNameFromUri(contentResolver, uri)
+ val tempFile = File(context.cacheDir, fileName)
+
+ return try {
+ contentResolver.openInputStream(uri)?.use { inputStream ->
+ FileOutputStream(tempFile).use { outputStream ->
+ inputStream.copyTo(outputStream)
+ }
+ }
+ tempFile.absolutePath
+ } catch (e: Exception) {
+ e.printStackTrace()
+ null
+ }
+ }
+
+ private fun getFileNameFromUri(contentResolver: ContentResolver, uri: Uri): String {
+ var name = "temp_file"
+ val returnCursor = contentResolver.query(uri, null, null, null, null)
+ returnCursor?.use {
+ val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
+ if (it.moveToFirst() && nameIndex >= 0) {
+ name = it.getString(nameIndex)
+ }
+ }
+ return name
+ }
+
+ fun getDurationFromFile(file: File): Long {
+ val retriever = MediaMetadataRetriever()
+ return try {
+ retriever.setDataSource(file.absolutePath)
+ retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
+ } catch (e: Exception) {
+ e.printStackTrace()
+ 0L
+ } finally {
+ retriever.release()
+ }
+ }
}
\ No newline at end of file
diff --git a/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FFmpegQueryExtension.kt b/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FFmpegQueryExtension.kt
index ba5f614..55367f3 100644
--- a/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FFmpegQueryExtension.kt
+++ b/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FFmpegQueryExtension.kt
@@ -46,7 +46,7 @@ public class FFmpegQueryExtension {
add("-i")
add(input)
add("-s")
- add("${width}x${height}")
+ add("${((width ?: 0)/2) * 2}x${((height ?: 0)/2) * 2}") // Ensure width and height are even numbers
add("-vf")
add("format=yuv420p,$fade")
add("-t")
diff --git a/app/build.gradle b/app/build.gradle
index cbfac26..d1c6cab 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,35 +1,57 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
- id 'kotlin-android-extensions'
id 'kotlin-kapt'
}
android {
- compileSdkVersion 31
-
+ namespace 'com.simform.videoimageeditor'
+ compileSdk 35
defaultConfig {
applicationId "com.simform.videoimageeditor"
minSdkVersion 24
- targetSdkVersion 31
+ targetSdkVersion 35
versionCode 1
versionName "1.0"
- testInstrumentationRunner "androidx...test.runner.AndroidJUnitRunner"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ signingConfigs {
+ debug {
+ storeFile file("${System.getProperty('user.home')}/.android/debug.keystore")
+ storePassword 'android'
+ keyAlias 'androiddebugkey'
+ keyPassword 'android'
+ }
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
}
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
kotlinOptions {
jvmTarget = '11'
}
+
+ buildFeatures {
+ viewBinding true
+ }
+
flavorDimensions "default"
+}
- androidExtensions {
- experimental = true
+repositories {
+ flatDir {
+ dirs '../SSffmpegVideoOperation/libs'
}
}
@@ -44,13 +66,15 @@ kapt {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- implementation 'androidx.core:core-ktx:1.8.0'
- implementation 'androidx.appcompat:appcompat:1.4.2'
- implementation 'com.github.bumptech.glide:glide:4.12.0'
- annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
- implementation 'com.google.android.material:material:1.6.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'androidx.core:core-ktx:1.9.0'
+ implementation 'androidx.appcompat:appcompat:1.7.1'
+ implementation 'com.github.bumptech.glide:glide:4.16.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
+ implementation 'com.google.android.material:material:1.12.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'pub.devrel:easypermissions:3.0.0'
implementation 'com.github.jaiselrahman:FilePicker:1.3.2'
implementation project(':SSffmpegVideoOperation')
+ implementation "androidx.activity:activity-ktx:1.10.1"
+ implementation "androidx.fragment:fragment-ktx:1.8.8"
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 388fb50..a469178 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,8 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.simform.videoimageeditor">
-
-
+
diff --git a/app/src/main/java/com/simform/videoimageeditor/BaseActivity.kt b/app/src/main/java/com/simform/videoimageeditor/BaseActivity.kt
index 4ed6c78..44f70fc 100644
--- a/app/src/main/java/com/simform/videoimageeditor/BaseActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/BaseActivity.kt
@@ -2,12 +2,16 @@ package com.simform.videoimageeditor
import android.content.Intent
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.os.Bundle
import android.view.MenuItem
import android.view.View
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.jaiselrahman.filepicker.activity.FilePickerActivity
import com.jaiselrahman.filepicker.model.MediaFile
+import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.FileSelection
@@ -24,12 +28,108 @@ abstract class BaseActivity(view: Int, title: Int) : AppCompatActivity(), View.O
var retriever: MediaMetadataRetriever? = null
val utils = Utils()
val ffmpegQueryExtension = FFmpegQueryExtension()
+ val pickSingleMedia =
+ registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
+ if (uri != null) {
+ val mediaType = when (contentResolver.getType(uri)?.substringBefore('/')) {
+ "image" -> MediaFile.TYPE_IMAGE
+ "video" -> MediaFile.TYPE_VIDEO
+ "audio" -> MediaFile.TYPE_AUDIO
+ else -> MediaFile.TYPE_FILE
+ }
+
+ val mediaFile = MediaFile().apply {
+ setUri(uri)
+ setMediaType(mediaType)
+ setMimeType(contentResolver.getType(uri))
+ setName(uri.lastPathSegment ?: "Unknown")
+ }
+
+ val requestCode = when (mediaType) {
+ MediaFile.TYPE_IMAGE -> Common.IMAGE_FILE_REQUEST_CODE
+ MediaFile.TYPE_VIDEO -> Common.VIDEO_FILE_REQUEST_CODE
+ MediaFile.TYPE_AUDIO -> Common.AUDIO_FILE_REQUEST_CODE
+ else -> Common.VIDEO_FILE_REQUEST_CODE // Default to video for unknown types
+ }
+
+ val mediaFiles = listOf(mediaFile)
+ this.mediaFiles = mediaFiles
+ (this as FileSelection).selectedFiles(mediaFiles, requestCode)
+ } else {
+ Toast.makeText(this, "User cancelled the selection", Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ val pickMultipleMedia = registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) { uris ->
+ if (uris.isNotEmpty()) {
+ val mediaFiles = uris.map { uri ->
+ val mediaType = when (contentResolver.getType(uri)?.substringBefore('/')) {
+ "image" -> MediaFile.TYPE_IMAGE
+ "video" -> MediaFile.TYPE_VIDEO
+ "audio" -> MediaFile.TYPE_AUDIO
+ else -> MediaFile.TYPE_FILE
+ }
+
+ MediaFile().apply {
+ setUri(uri)
+ setMediaType(mediaType)
+ setMimeType(contentResolver.getType(uri))
+ setName(uri.lastPathSegment ?: "Unknown")
+ }
+ }
+ val requestCode = when (contentResolver.getType(mediaFiles.first().uri)?.substringBefore('/')) {
+ "image" -> Common.IMAGE_FILE_REQUEST_CODE
+ "video" -> Common.VIDEO_FILE_REQUEST_CODE
+ "audio" -> Common.AUDIO_FILE_REQUEST_CODE
+ else -> Common.VIDEO_FILE_REQUEST_CODE// Default to video for unknown types
+ }
+ this.mediaFiles = mediaFiles
+ (this as FileSelection).selectedFiles(mediaFiles, requestCode)
+ } else {
+ Toast.makeText(this, "User cancelled the selection", Toast.LENGTH_SHORT).show()
+ }
+
+ }
+
+ val pickAudio = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
+ if (uri != null) {
+ // Got audio Uri, do something with it
+ val mediaFiles = listOf(MediaFile().apply {
+ setUri(uri)
+ setMediaType(MediaFile.TYPE_AUDIO)
+ setMimeType(contentResolver.getType(uri))
+ setName(uri.lastPathSegment ?: "Unknown")
+ })
+ this.mediaFiles = mediaFiles
+ (this as FileSelection).selectedFiles(mediaFiles, Common.AUDIO_FILE_REQUEST_CODE)
+ } else {
+ Toast.makeText(this, "No audio selected", Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ val pickMultipleAudio = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
+ if (uris != null) {
+ // Got audio Uri, do something with it
+ val mediaFiles = uris.map { uri ->
+ MediaFile().apply {
+ setUri(uri)
+ setMediaType(MediaFile.TYPE_AUDIO)
+ setMimeType(contentResolver.getType(uri))
+ setName(uri.lastPathSegment ?: "Unknown")
+ }
+ }
+ this.mediaFiles = mediaFiles
+ (this as FileSelection).selectedFiles(mediaFiles, Common.AUDIO_FILE_REQUEST_CODE)
+ } else {
+ Toast.makeText(this, "No audio selected", Toast.LENGTH_SHORT).show()
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(layoutView)
- utils.addSupportActionBar(this, toolbarTitle)
+ // Content view will be set by individual activities using view binding
initialization()
+ utils.addSupportActionBar(this, toolbarTitle)
}
protected abstract fun initialization()
@@ -43,7 +143,7 @@ abstract class BaseActivity(view: Int, title: Int) : AppCompatActivity(), View.O
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
- if (resultCode == RESULT_OK && data != null) {
+ if (resultCode == RESULT_OK && data != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
mediaFiles = data.getParcelableArrayListExtra(FilePickerActivity.MEDIA_FILES)
(this as FileSelection).selectedFiles(mediaFiles,requestCode)
}
diff --git a/app/src/main/java/com/simform/videoimageeditor/MainActivity.kt b/app/src/main/java/com/simform/videoimageeditor/MainActivity.kt
index 39905d7..76fcdaa 100644
--- a/app/src/main/java/com/simform/videoimageeditor/MainActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/MainActivity.kt
@@ -1,18 +1,27 @@
package com.simform.videoimageeditor
import android.view.View
+import androidx.activity.enableEdgeToEdge
+import com.simform.videoimageeditor.databinding.ActivityMainBinding
import com.simform.videoimageeditor.middlewareActivity.OtherFFMPEGProcessActivity
import com.simform.videoimageeditor.middlewareActivity.VideoProcessActivity
-import kotlinx.android.synthetic.main.activity_main.imageGifOperation
-import kotlinx.android.synthetic.main.activity_main.videoOperation
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
class MainActivity : BaseActivity(R.layout.activity_main, R.string.ffpmeg_title) {
+
+ private lateinit var binding: ActivityMainBinding
+
override fun initialization() {
- supportActionBar?.title = getString(R.string.ffpmeg_title)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.ffpmeg_title)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
supportActionBar?.setDisplayShowHomeEnabled(false)
- videoOperation.setOnClickListener(this)
- imageGifOperation.setOnClickListener(this)
+ binding.apply {
+ videoOperation.setOnClickListener(this@MainActivity)
+ imageGifOperation.setOnClickListener(this@MainActivity)
+ }
}
override fun onClick(v: View?) {
diff --git a/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/OtherFFMPEGProcessActivity.kt b/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/OtherFFMPEGProcessActivity.kt
index 62460af..33a39f4 100644
--- a/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/OtherFFMPEGProcessActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/OtherFFMPEGProcessActivity.kt
@@ -1,30 +1,35 @@
package com.simform.videoimageeditor.middlewareActivity
import android.view.View
+import androidx.activity.enableEdgeToEdge
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityOtherFfmpegProcessBinding
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.AudiosMergeActivity
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.ChangeAudioVolumeActivity
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.CompressAudioActivity
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.CropAudioActivity
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.FastAndSlowAudioActivity
import com.simform.videoimageeditor.otherFFMPEGProcessActivity.MergeGIFActivity
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnAudiosVolumeUpdate
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnCompressAudio
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnCutAudio
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnFastAndSlowAudio
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnMergeAudios
-import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnMergeGIF
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
class OtherFFMPEGProcessActivity : BaseActivity(R.layout.activity_other_ffmpeg_process, R.string.other_ffmpeg_operations) {
+
+ private lateinit var binding: ActivityOtherFfmpegProcessBinding
+
override fun initialization() {
- supportActionBar?.title = getString(R.string.other_ffmpeg_operations)
- btnMergeGIF.setOnClickListener(this)
- btnMergeAudios.setOnClickListener(this)
- btnAudiosVolumeUpdate.setOnClickListener(this)
- btnFastAndSlowAudio.setOnClickListener(this)
- btnCutAudio.setOnClickListener(this)
- btnCompressAudio.setOnClickListener(this)
+ binding = ActivityOtherFfmpegProcessBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.other_ffmpeg_operations)
+ binding.apply {
+ btnMergeGIF.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ btnMergeAudios.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ btnAudiosVolumeUpdate.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ btnFastAndSlowAudio.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ btnCutAudio.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ btnCompressAudio.setOnClickListener(this@OtherFFMPEGProcessActivity)
+ }
}
override fun onClick(v: View?) {
diff --git a/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/VideoProcessActivity.kt b/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/VideoProcessActivity.kt
index a265433..604bddc 100644
--- a/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/VideoProcessActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/VideoProcessActivity.kt
@@ -3,35 +3,42 @@ package com.simform.videoimageeditor.middlewareActivity
import android.view.View
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityVideoProcessBinding
import com.simform.videoimageeditor.videoProcessActivity.*
-import kotlinx.android.synthetic.main.activity_video_process.*
/**
* Created by Ashvin Vavaliya on 29,December,2020
* Simform Solutions Pvt Ltd.
*/
class VideoProcessActivity : BaseActivity(R.layout.activity_video_process, R.string.video_operations) {
+ private lateinit var binding: ActivityVideoProcessBinding
+
override fun initialization() {
+ binding = ActivityVideoProcessBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
supportActionBar?.title = getString(R.string.video_operations)
- btnCutVideo.setOnClickListener(this)
- btnImageToVideo.setOnClickListener(this)
- btnAddWaterMarkOnVideo.setOnClickListener(this)
- btnCombineImageVideo.setOnClickListener(this)
- btnCombineImages.setOnClickListener(this)
- btnCombineVideos.setOnClickListener(this)
- btnCompressVideo.setOnClickListener(this)
- btnExtractVideo.setOnClickListener(this)
- btnExtractAudio.setOnClickListener(this)
- btnMotion.setOnClickListener(this)
- btnReverseVideo.setOnClickListener(this)
- btnFadeInFadeOutVideo.setOnClickListener(this)
- btnVideoConvertIntoGIF.setOnClickListener(this)
- btnVideoRotateFlip.setOnClickListener(this)
- btnMergeVideoAndAudio.setOnClickListener(this)
- btnAddTextOnVideo.setOnClickListener(this)
- btnRemoveAudioFromVideo.setOnClickListener(this)
- btnMergeImageAndAudio.setOnClickListener(this)
- btnSetAspectRatio.setOnClickListener(this)
+ binding.apply {
+ btnCutVideo.setOnClickListener(this@VideoProcessActivity)
+ btnImageToVideo.setOnClickListener(this@VideoProcessActivity)
+ btnAddWaterMarkOnVideo.setOnClickListener(this@VideoProcessActivity)
+ btnCombineImageVideo.setOnClickListener(this@VideoProcessActivity)
+ btnCombineImages.setOnClickListener(this@VideoProcessActivity)
+ btnCombineVideos.setOnClickListener(this@VideoProcessActivity)
+ btnCompressVideo.setOnClickListener(this@VideoProcessActivity)
+ btnExtractVideo.setOnClickListener(this@VideoProcessActivity)
+ btnExtractAudio.setOnClickListener(this@VideoProcessActivity)
+ btnMotion.setOnClickListener(this@VideoProcessActivity)
+ btnReverseVideo.setOnClickListener(this@VideoProcessActivity)
+ btnFadeInFadeOutVideo.setOnClickListener(this@VideoProcessActivity)
+ btnVideoConvertIntoGIF.setOnClickListener(this@VideoProcessActivity)
+ btnVideoRotateFlip.setOnClickListener(this@VideoProcessActivity)
+ btnMergeVideoAndAudio.setOnClickListener(this@VideoProcessActivity)
+ btnAddTextOnVideo.setOnClickListener(this@VideoProcessActivity)
+ btnRemoveAudioFromVideo.setOnClickListener(this@VideoProcessActivity)
+ btnMergeImageAndAudio.setOnClickListener(this@VideoProcessActivity)
+ btnSetAspectRatio.setOnClickListener(this@VideoProcessActivity)
+ }
}
override fun onClick(v: View?) {
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/AudiosMergeActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/AudiosMergeActivity.kt
index 020c436..3d7e548 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/AudiosMergeActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/AudiosMergeActivity.kt
@@ -1,35 +1,47 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityAudiosMergeBinding
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.DURATION_FIRST
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import com.simform.videooperations.Paths
-import kotlinx.android.synthetic.main.activity_audios_merge.btnAudioPath
-import kotlinx.android.synthetic.main.activity_audios_merge.btnMerge
-import kotlinx.android.synthetic.main.activity_audios_merge.mProgressView
-import kotlinx.android.synthetic.main.activity_audios_merge.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_audios_merge.tvOutputPath
class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.string.merge_audios) {
+ private lateinit var binding: ActivityAudiosMergeBinding
private var isInputAudioSelected: Boolean = false
+
override fun initialization() {
- btnAudioPath.setOnClickListener(this)
- btnMerge.setOnClickListener(this)
+ binding = ActivityAudiosMergeBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.merge_audios)
+ binding.btnAudioPath.setOnClickListener(this)
+ binding.btnMerge.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnAudioPath -> {
- Common.selectFile(this, maxSelection = 10, isImageSelection = false, isAudioSelection = true)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickMultipleAudio.launch(arrayOf("audio/*"))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 10,
+ isImageSelection = false,
+ isAudioSelection = true
+ )
+ }
}
R.id.btnMerge -> {
mediaFiles?.size?.let {
@@ -50,7 +62,12 @@ class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.strin
mediaFiles?.let {
for (element in it) {
val paths = Paths()
- paths.filePath = element.path
+ paths.filePath =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, element.uri) ?: ""
+ } else {
+ element.path ?: ""
+ }
paths.isImageFile = true
pathsList.add(paths)
}
@@ -59,11 +76,11 @@ class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.strin
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -79,15 +96,19 @@ class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.strin
}
private fun processStop() {
- btnAudioPath.isEnabled = true
- btnMerge.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnAudioPath.isEnabled = true
+ btnMerge.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnAudioPath.isEnabled = false
- btnMerge.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnAudioPath.isEnabled = false
+ btnMerge.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
@SuppressLint("NewApi")
@@ -97,7 +118,7 @@ class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.strin
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
val size: Int = mediaFiles.size
if (size > 1) {
- tvInputPathAudio.text = "$size Audio selected"
+ binding.tvInputPathAudio.text = "$size Audio selected"
isInputAudioSelected = true
} else {
Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/ChangeAudioVolumeActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/ChangeAudioVolumeActivity.kt
index f40ceb0..a84ae06 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/ChangeAudioVolumeActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/ChangeAudioVolumeActivity.kt
@@ -1,33 +1,45 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityChangeAudioValumeBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_change_audio_valume.btnAudioPath
-import kotlinx.android.synthetic.main.activity_change_audio_valume.btnChange
-import kotlinx.android.synthetic.main.activity_change_audio_valume.mProgressView
-import kotlinx.android.synthetic.main.activity_change_audio_valume.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_change_audio_valume.tvOutputPath
class ChangeAudioVolumeActivity : BaseActivity(R.layout.activity_change_audio_valume, R.string.change_audio_volume) {
+ private lateinit var binding: ActivityChangeAudioValumeBinding
private var isInputAudioSelected: Boolean = false
+
override fun initialization() {
- btnAudioPath.setOnClickListener(this)
- btnChange.setOnClickListener(this)
+ binding = ActivityChangeAudioValumeBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnAudioPath.setOnClickListener(this@ChangeAudioVolumeActivity)
+ btnChange.setOnClickListener(this@ChangeAudioVolumeActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnAudioPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickAudio.launch(arrayOf("audio/*"))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 1,
+ isImageSelection = false,
+ isAudioSelection = true
+ )
+ }
}
R.id.btnChange -> {
mediaFiles?.size?.let {
@@ -44,14 +56,14 @@ class ChangeAudioVolumeActivity : BaseActivity(R.layout.activity_change_audio_va
private fun mergeAudioProcess() {
val outputPath = Common.getFilePath(this, Common.MP3)
- val query = ffmpegQueryExtension.audioVolumeUpdate(tvInputPathAudio.text.toString(), volume = 0.1f, output = outputPath)
+ val query = ffmpegQueryExtension.audioVolumeUpdate(binding.tvInputPathAudio.text.toString(), volume = 0.1f, output = outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -66,15 +78,19 @@ class ChangeAudioVolumeActivity : BaseActivity(R.layout.activity_change_audio_va
}
private fun processStop() {
- btnAudioPath.isEnabled = true
- btnChange.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnAudioPath.isEnabled = true
+ btnChange.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnAudioPath.isEnabled = false
- btnChange.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnAudioPath.isEnabled = false
+ btnChange.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
@SuppressLint("NewApi")
@@ -82,7 +98,12 @@ class ChangeAudioVolumeActivity : BaseActivity(R.layout.activity_change_audio_va
when (requestCode) {
Common.AUDIO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathAudio.text = mediaFiles[0].path
+ binding.tvInputPathAudio.text =
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path ?: ""
+ }
isInputAudioSelected = true
} else {
Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CompressAudioActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CompressAudioActivity.kt
index 8e67c43..2cf3ce8 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CompressAudioActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CompressAudioActivity.kt
@@ -1,34 +1,47 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCompressAudioBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.BITRATE_128
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_compress_audio.btnAudioPath
-import kotlinx.android.synthetic.main.activity_compress_audio.btnChange
-import kotlinx.android.synthetic.main.activity_compress_audio.mProgressView
-import kotlinx.android.synthetic.main.activity_compress_audio.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_compress_audio.tvOutputPath
class CompressAudioActivity : BaseActivity(R.layout.activity_compress_audio, R.string.compress_audio) {
+ private lateinit var binding: ActivityCompressAudioBinding
private var isInputAudioSelected: Boolean = false
+
override fun initialization() {
- btnAudioPath.setOnClickListener(this)
- btnChange.setOnClickListener(this)
+ binding = ActivityCompressAudioBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnAudioPath.setOnClickListener(this@CompressAudioActivity)
+ btnChange.setOnClickListener(this@CompressAudioActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnAudioPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickAudio.launch(arrayOf("audio/*"))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 1,
+ isImageSelection = false,
+ isAudioSelection = true
+ )
+ }
}
R.id.btnChange -> {
mediaFiles?.size?.let {
@@ -45,14 +58,14 @@ class CompressAudioActivity : BaseActivity(R.layout.activity_compress_audio, R.s
private fun compressAudioProcess() {
val outputPath = Common.getFilePath(this, Common.MP3)
- val query = ffmpegQueryExtension.compressAudio(inputAudioPath = tvInputPathAudio.text.toString(), bitrate = BITRATE_128, output = outputPath)
+ val query = ffmpegQueryExtension.compressAudio(inputAudioPath = binding.tvInputPathAudio.text.toString(), bitrate = BITRATE_128, output = outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -67,15 +80,19 @@ class CompressAudioActivity : BaseActivity(R.layout.activity_compress_audio, R.s
}
private fun processStop() {
- btnAudioPath.isEnabled = true
- btnChange.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnAudioPath.isEnabled = true
+ btnChange.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnAudioPath.isEnabled = false
- btnChange.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnAudioPath.isEnabled = false
+ btnChange.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
@SuppressLint("NewApi")
@@ -83,7 +100,12 @@ class CompressAudioActivity : BaseActivity(R.layout.activity_compress_audio, R.s
when (requestCode) {
Common.AUDIO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathAudio.text = mediaFiles[0].path
+ binding.tvInputPathAudio.text =
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path ?: ""
+ }
isInputAudioSelected = true
} else {
Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CropAudioActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CropAudioActivity.kt
index 7eb8f79..5287667 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CropAudioActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CropAudioActivity.kt
@@ -1,6 +1,7 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.TextView
@@ -8,53 +9,63 @@ import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCropAudioBinding
import com.simform.videoimageeditor.ikovac.timepickerwithseconds.MyTimePickerDialog
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
+import com.simform.videooperations.Common.stringForTime
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.LogMessage
+import java.io.File
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
-import kotlinx.android.synthetic.main.activity_crop_audio.btnAudioPath
-import kotlinx.android.synthetic.main.activity_crop_audio.btnConvert
-import kotlinx.android.synthetic.main.activity_crop_audio.btnSelectEndTime
-import kotlinx.android.synthetic.main.activity_crop_audio.btnSelectStartTime
-import kotlinx.android.synthetic.main.activity_crop_audio.edtEndTime
-import kotlinx.android.synthetic.main.activity_crop_audio.edtStartTime
-import kotlinx.android.synthetic.main.activity_crop_audio.mProgressView
-import kotlinx.android.synthetic.main.activity_crop_audio.tvInputPath
-import kotlinx.android.synthetic.main.activity_crop_audio.tvMaxTime
-import kotlinx.android.synthetic.main.activity_crop_audio.tvOutputPath
class CropAudioActivity : BaseActivity(R.layout.activity_crop_audio, R.string.crop_audio_using_time) {
private var startTimeString: String? = null
private var endTimeString: String? = null
private var maxTimeString: String? = null
+ private lateinit var binding: ActivityCropAudioBinding
override fun initialization() {
- btnAudioPath.setOnClickListener(this)
- btnSelectStartTime.setOnClickListener(this)
- btnSelectEndTime.setOnClickListener(this)
- btnConvert.setOnClickListener(this)
+ binding = ActivityCropAudioBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.crop_audio_using_time)
+ binding.apply {
+ btnAudioPath.setOnClickListener(this@CropAudioActivity)
+ btnSelectStartTime.setOnClickListener(this@CropAudioActivity)
+ btnSelectEndTime.setOnClickListener(this@CropAudioActivity)
+ btnConvert.setOnClickListener(this@CropAudioActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnAudioPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickAudio.launch(arrayOf("audio/*"))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 1,
+ isImageSelection = false,
+ isAudioSelection = true
+ )
+ }
}
R.id.btnSelectStartTime -> {
if (!TextUtils.isEmpty(maxTimeString) && !TextUtils.equals(maxTimeString, getString(R.string.zero_time))) {
- selectTime(edtStartTime, true)
+ selectTime(binding.edtStartTime, true)
} else {
Toast.makeText(this, getString(R.string.input_audio_validate_message), Toast.LENGTH_SHORT).show()
}
}
R.id.btnSelectEndTime -> {
if (!TextUtils.isEmpty(maxTimeString) && !TextUtils.equals(maxTimeString, getString(R.string.zero_time))) {
- selectTime(edtEndTime, false)
+ selectTime(binding.edtEndTime, false)
} else {
Toast.makeText(this, getString(R.string.input_audio_validate_message), Toast.LENGTH_SHORT).show()
}
@@ -87,9 +98,21 @@ class CropAudioActivity : BaseActivity(R.layout.activity_crop_audio, R.string.cr
override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
if (requestCode == Common.AUDIO_FILE_REQUEST_CODE) {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPath.text = mediaFiles[0].path
- maxTimeString = Common.stringForTime(mediaFiles[0].duration)
- tvMaxTime.text = "Selected audio max time : $maxTimeString"
+ binding.tvInputPath.text =
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path ?: ""
+ }
+ maxTimeString =
+ stringForTime(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.getDurationFromFile(File(binding.tvInputPath.text.toString()))
+ } else {
+ mediaFiles[0].duration
+ }
+ )
+ binding.tvMaxTime.text = "Selected audio max time : $maxTimeString"
} else {
Toast.makeText(this, getString(R.string.audio_not_selected_toast_message), Toast.LENGTH_SHORT).show()
}
@@ -145,14 +168,14 @@ class CropAudioActivity : BaseActivity(R.layout.activity_crop_audio, R.string.cr
@SuppressLint("SetTextI18n")
private fun cutProcess() {
val outputPath = Common.getFilePath(this, Common.MP3)
- val query = ffmpegQueryExtension.cutAudio(tvInputPath.text.toString(), startTimeString, endTimeString, outputPath)
+ val query = ffmpegQueryExtension.cutAudio(binding.tvInputPath.text.toString(), startTimeString, endTimeString, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -167,18 +190,22 @@ class CropAudioActivity : BaseActivity(R.layout.activity_crop_audio, R.string.cr
}
private fun processStop() {
- btnAudioPath.isEnabled = true
- btnSelectStartTime.isEnabled = true
- btnSelectEndTime.isEnabled = true
- btnConvert.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnAudioPath.isEnabled = true
+ btnSelectStartTime.isEnabled = true
+ btnSelectEndTime.isEnabled = true
+ btnConvert.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnAudioPath.isEnabled = false
- btnSelectStartTime.isEnabled = false
- btnSelectEndTime.isEnabled = false
- btnConvert.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnAudioPath.isEnabled = false
+ btnSelectStartTime.isEnabled = false
+ btnSelectEndTime.isEnabled = false
+ btnConvert.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/FastAndSlowAudioActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/FastAndSlowAudioActivity.kt
index ff07164..b77153a 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/FastAndSlowAudioActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/FastAndSlowAudioActivity.kt
@@ -1,33 +1,45 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
+import android.os.Build
import android.view.View
import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityFastAndSlowAudioBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.btnAudioPath
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.btnMotion
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.mProgressView
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.motionType
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.tvOutputPath
class FastAndSlowAudioActivity : BaseActivity(R.layout.activity_fast_and_slow_audio, R.string.fast_slow_motion_video) {
+ private lateinit var binding: ActivityFastAndSlowAudioBinding
private var isInputAudioSelected: Boolean = false
+
override fun initialization() {
- btnAudioPath.setOnClickListener(this)
- btnMotion.setOnClickListener(this)
+ binding = ActivityFastAndSlowAudioBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnAudioPath.setOnClickListener(this@FastAndSlowAudioActivity)
+ btnMotion.setOnClickListener(this@FastAndSlowAudioActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnAudioPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickAudio.launch(arrayOf("audio/*"))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 1,
+ isImageSelection = false,
+ isAudioSelection = true
+ )
+ }
}
R.id.btnMotion -> {
when {
@@ -46,17 +58,17 @@ class FastAndSlowAudioActivity : BaseActivity(R.layout.activity_fast_and_slow_au
private fun motionProcess() {
val outputPath = Common.getFilePath(this, Common.MP3)
var atempo = 2.0
- if (!motionType.isChecked) {
+ if (!binding.motionType.isChecked) {
atempo = 0.5
}
- val query = ffmpegQueryExtension.audioMotion(tvInputPathAudio.text.toString(), outputPath, atempo)
+ val query = ffmpegQueryExtension.audioMotion(binding.tvInputPathAudio.text.toString(), outputPath, atempo)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -74,7 +86,12 @@ class FastAndSlowAudioActivity : BaseActivity(R.layout.activity_fast_and_slow_au
when (requestCode) {
Common.AUDIO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathAudio.text = mediaFiles[0].path
+ binding.tvInputPathAudio.text =
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path ?: ""
+ }
isInputAudioSelected = true
} else {
Toast.makeText(this, getString(R.string.audio_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -84,14 +101,18 @@ class FastAndSlowAudioActivity : BaseActivity(R.layout.activity_fast_and_slow_au
}
private fun processStop() {
- btnAudioPath.isEnabled = true
- btnMotion.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnAudioPath.isEnabled = true
+ btnMotion.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnAudioPath.isEnabled = false
- btnMotion.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnAudioPath.isEnabled = false
+ btnMotion.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/MergeGIFActivity.kt b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/MergeGIFActivity.kt
index d30e8ac..0b31abd 100644
--- a/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/MergeGIFActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/MergeGIFActivity.kt
@@ -1,6 +1,7 @@
package com.simform.videoimageeditor.otherFFMPEGProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.ImageView
@@ -14,69 +15,79 @@ import com.bumptech.glide.request.transition.Transition
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityMergeGifBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import com.simform.videooperations.Paths
import java.io.File
-import kotlinx.android.synthetic.main.activity_merge_gif.btnGifPath
-import kotlinx.android.synthetic.main.activity_merge_gif.btnMerge
-import kotlinx.android.synthetic.main.activity_merge_gif.edtXPos
-import kotlinx.android.synthetic.main.activity_merge_gif.edtXScale
-import kotlinx.android.synthetic.main.activity_merge_gif.edtYPos
-import kotlinx.android.synthetic.main.activity_merge_gif.edtYScale
-import kotlinx.android.synthetic.main.activity_merge_gif.mFirstGif
-import kotlinx.android.synthetic.main.activity_merge_gif.mProgressView
-import kotlinx.android.synthetic.main.activity_merge_gif.tvInputPathGif
-import kotlinx.android.synthetic.main.activity_merge_gif.tvOutputPath
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
class MergeGIFActivity : BaseActivity(R.layout.activity_merge_gif, R.string.merge_gif) {
+ private lateinit var binding: ActivityMergeGifBinding
private var isInputGifSelected: Boolean = false
+
override fun initialization() {
- btnGifPath.setOnClickListener(this)
- btnMerge.setOnClickListener(this)
+ binding = ActivityMergeGifBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnGifPath.setOnClickListener(this@MergeGIFActivity)
+ btnMerge.setOnClickListener(this@MergeGIFActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnGifPath -> {
- Common.selectFile(this, maxSelection = 2, isImageSelection = true, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 2,
+ isImageSelection = true,
+ isAudioSelection = false
+ )
+ }
}
R.id.btnMerge -> {
- when {
- !isInputGifSelected -> {
- Toast.makeText(this, getString(R.string.input_gif_validate_message), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtXPos.text.toString()) -> {
- Toast.makeText(this, getString(R.string.x_position_validation), Toast.LENGTH_SHORT).show()
- }
- edtXPos.text.toString().toFloat() > 100 || edtXPos.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.x_validation_invalid), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtYPos.text.toString()) -> {
- Toast.makeText(this, getString(R.string.y_position_validation), Toast.LENGTH_SHORT).show()
- }
- edtYPos.text.toString().toFloat() > 100 || edtYPos.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.y_validation_invalid), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtXScale.text.toString()) -> {
- Toast.makeText(this, getString(R.string.x_width_validation), Toast.LENGTH_SHORT).show()
- }
- edtXScale.text.toString().toFloat() > 100 || edtXScale.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.x_width_invalid), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtYScale.text.toString()) -> {
- Toast.makeText(this, getString(R.string.y_height_validation), Toast.LENGTH_SHORT).show()
- }
- edtYScale.text.toString().toFloat() > 100 || edtYScale.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.y_height_invalid), Toast.LENGTH_SHORT).show()
- }
- else -> {
- processStart()
- combineGifProcess()
+ with(binding) {
+ when {
+ !isInputGifSelected -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.input_gif_validate_message), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtXPos.text.toString()) -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.x_position_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtXPos.text.toString().toFloat() > 100 || edtXPos.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.x_validation_invalid), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtYPos.text.toString()) -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.y_position_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtYPos.text.toString().toFloat() > 100 || edtYPos.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.y_validation_invalid), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtXScale.text.toString()) -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.x_width_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtXScale.text.toString().toFloat() > 100 || edtXScale.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.x_width_invalid), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtYScale.text.toString()) -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.y_height_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtYScale.text.toString().toFloat() > 100 || edtYScale.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@MergeGIFActivity, getString(R.string.y_height_invalid), Toast.LENGTH_SHORT).show()
+ }
+ else -> {
+ processStart()
+ combineGifProcess()
+ }
}
}
}
@@ -89,33 +100,38 @@ class MergeGIFActivity : BaseActivity(R.layout.activity_merge_gif, R.string.merg
mediaFiles?.let {
for (element in it) {
val paths = Paths()
- paths.filePath = element.path
+ paths.filePath =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, element.uri) ?: ""
+ } else {
+ element.path
+ }
paths.isImageFile = true
pathsList.add(paths)
}
val xPos = width?.let { width ->
- (edtXPos.text.toString().toFloat().times(width)).div(100)
+ (binding.edtXPos.text.toString().toFloat().times(width)).div(100)
}
val yPos = height?.let { height ->
- (edtYPos.text.toString().toFloat().times(height)).div(100)
+ (binding.edtYPos.text.toString().toFloat().times(height)).div(100)
}
val widthScale = width?.let { width ->
- (edtXScale.text.toString().toFloat().times(width)).div(100)
+ (binding.edtXScale.text.toString().toFloat().times(width)).div(100)
}
val heightScale = height?.let { height ->
- (edtYScale.text.toString().toFloat().times(height)).div(100)
+ (binding.edtYScale.text.toString().toFloat().times(height)).div(100)
}
val query = ffmpegQueryExtension.mergeGIF(pathsList, xPos, yPos, widthScale, heightScale, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -131,15 +147,19 @@ class MergeGIFActivity : BaseActivity(R.layout.activity_merge_gif, R.string.merg
}
private fun processStop() {
- btnGifPath.isEnabled = true
- btnMerge.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnGifPath.isEnabled = true
+ btnMerge.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnGifPath.isEnabled = false
- btnMerge.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnGifPath.isEnabled = false
+ btnMerge.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
@SuppressLint("NewApi")
@@ -150,8 +170,15 @@ class MergeGIFActivity : BaseActivity(R.layout.activity_merge_gif, R.string.merg
val size: Int = mediaFiles.size
var isGifFile = true
for (i in 0 until size) {
- if (File(mediaFiles[i].path).extension != "gif") {
- isGifFile = false
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (mediaFiles[i].mimeType != "image/gif") {
+ isGifFile = false
+ }
+ } else {
+ // For older versions, check the file extension
+ if (File(mediaFiles[i].path).extension != "gif") {
+ isGifFile = false
+ }
}
}
if (size == 2 && isGifFile) {
@@ -159,13 +186,13 @@ class MergeGIFActivity : BaseActivity(R.layout.activity_merge_gif, R.string.merg
.asGif()
.apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.RESOURCE))
.load(mediaFiles[0].path)
- .into(object : ViewTarget(mFirstGif) {
+ .into(object : ViewTarget(binding.mFirstGif) {
override fun onResourceReady(gifDrawable: GifDrawable, transition: Transition?) {
width = gifDrawable.intrinsicWidth
height = gifDrawable.intrinsicHeight
}
})
- tvInputPathGif.text = "$size GIF selected"
+ binding.tvInputPathGif.text = "$size GIF selected"
isInputGifSelected = true
} else if (size != 2) {
Toast.makeText(this, getString(R.string.please_selected_minimum_2_gif_file), Toast.LENGTH_SHORT).show()
diff --git a/app/src/main/java/com/simform/videoimageeditor/utils/ExtensionsUtils.kt b/app/src/main/java/com/simform/videoimageeditor/utils/ExtensionsUtils.kt
new file mode 100644
index 0000000..1e5ab18
--- /dev/null
+++ b/app/src/main/java/com/simform/videoimageeditor/utils/ExtensionsUtils.kt
@@ -0,0 +1,21 @@
+package com.simform.videoimageeditor.utils
+
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+
+fun AppCompatActivity.enableEdgeToEdge(view: View?) {
+ view?.let {
+ ViewCompat.setOnApplyWindowInsetsListener(it) { view, windowInsets ->
+ val systemBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ view.setPadding(
+ systemBarInsets.left,
+ systemBarInsets.top,
+ systemBarInsets.right,
+ 0
+ )
+ windowInsets
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddTextOnVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddTextOnVideoActivity.kt
index 2261a6e..d451aac 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddTextOnVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddTextOnVideoActivity.kt
@@ -2,65 +2,75 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityAddTextOnVideoBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.getFileFromAssets
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import java.util.concurrent.CompletableFuture.runAsync
-import kotlinx.android.synthetic.main.activity_add_text_on_video.btnAdd
-import kotlinx.android.synthetic.main.activity_add_text_on_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_add_text_on_video.edtText
-import kotlinx.android.synthetic.main.activity_add_text_on_video.edtXPos
-import kotlinx.android.synthetic.main.activity_add_text_on_video.edtYPos
-import kotlinx.android.synthetic.main.activity_add_text_on_video.mProgressView
-import kotlinx.android.synthetic.main.activity_add_text_on_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_add_text_on_video.tvOutputPath
class AddTextOnVideoActivity : BaseActivity(R.layout.activity_add_text_on_video, R.string.add_text_on_video) {
+ private lateinit var binding: ActivityAddTextOnVideoBinding
private var isInputVideoSelected = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnAdd.setOnClickListener(this)
+ binding = ActivityAddTextOnVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@AddTextOnVideoActivity)
+ btnAdd.setOnClickListener(this@AddTextOnVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 13
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnAdd -> {
- when {
- !isInputVideoSelected -> {
- Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtText.text.toString()) -> {
- Toast.makeText(this, getString(R.string.please_add_text_validation), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtXPos.text.toString()) -> {
- Toast.makeText(this, getString(R.string.x_position_validation), Toast.LENGTH_SHORT).show()
- }
- edtXPos.text.toString().toFloat() > 100 || edtXPos.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.x_validation_invalid), Toast.LENGTH_SHORT).show()
- }
- TextUtils.isEmpty(edtYPos.text.toString()) -> {
- Toast.makeText(this, getString(R.string.y_position_validation), Toast.LENGTH_SHORT).show()
- }
- edtYPos.text.toString().toFloat() > 100 || edtYPos.text.toString().toFloat() <= 0 -> {
- Toast.makeText(this, getString(R.string.y_validation_invalid), Toast.LENGTH_SHORT).show()
- }
- else -> {
- processStart()
- addTextProcess()
+ with(binding) {
+ when {
+ !isInputVideoSelected -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtText.text.toString()) -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.please_add_text_validation), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtXPos.text.toString()) -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.x_position_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtXPos.text.toString().toFloat() > 100 || edtXPos.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.x_validation_invalid), Toast.LENGTH_SHORT).show()
+ }
+ TextUtils.isEmpty(edtYPos.text.toString()) -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.y_position_validation), Toast.LENGTH_SHORT).show()
+ }
+ edtYPos.text.toString().toFloat() > 100 || edtYPos.text.toString().toFloat() <= 0 -> {
+ Toast.makeText(this@AddTextOnVideoActivity, getString(R.string.y_validation_invalid), Toast.LENGTH_SHORT).show()
+ }
+ else -> {
+ processStart()
+ addTextProcess()
+ }
}
}
+
}
}
}
@@ -68,20 +78,20 @@ class AddTextOnVideoActivity : BaseActivity(R.layout.activity_add_text_on_video,
private fun addTextProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
val xPos = width?.let {
- (edtXPos.text.toString().toFloat().times(it)).div(100)
+ (binding.edtXPos.text.toString().toFloat().times(it)).div(100)
}
val yPos = height?.let {
- (edtYPos.text.toString().toFloat().times(it)).div(100)
+ (binding.edtYPos.text.toString().toFloat().times(it)).div(100)
}
val fontPath = getFileFromAssets(this, "little_lord.ttf").absolutePath
- val query = ffmpegQueryExtension.addTextOnVideo(tvInputPathVideo.text.toString(), edtText.text.toString(), xPos, yPos, fontPath = fontPath, isTextBackgroundDisplay = true, fontSize = 28, fontcolor = "red", output = outputPath)
+ val query = ffmpegQueryExtension.addTextOnVideo(binding.tvInputPathVideo.text.toString(), binding.edtText.text.toString(), xPos, yPos, fontPath = fontPath, isTextBackgroundDisplay = true, fontSize = 28, fontcolor = "red", output = outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -96,15 +106,19 @@ class AddTextOnVideoActivity : BaseActivity(R.layout.activity_add_text_on_video,
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnAdd.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnAdd.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnAdd.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnAdd.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
@SuppressLint("NewApi")
@@ -112,11 +126,15 @@ class AddTextOnVideoActivity : BaseActivity(R.layout.activity_add_text_on_video,
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(binding.tvInputPathVideo.text.toString())
val bit = retriever?.frameAtTime
width = bit?.width
height = bit?.height
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddWaterMarkOnVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddWaterMarkOnVideoActivity.kt
index cad0a3a..77e8e60 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddWaterMarkOnVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AddWaterMarkOnVideoActivity.kt
@@ -2,12 +2,16 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityAddWaterMarkOnVideoBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.VIDEO
@@ -16,32 +20,40 @@ import com.simform.videooperations.Common.selectFile
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.LogMessage
import java.util.concurrent.CompletableFuture.runAsync
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.btnAdd
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.btnImagePath
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.edtXPos
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.edtYPos
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.mProgressView
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.tvInputPathImage
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_add_water_mark_on_video.tvOutputPath
class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mark_on_video, R.string.add_water_mark_on_video) {
+ private lateinit var binding: ActivityAddWaterMarkOnVideoBinding
private var isInputVideoSelected = false
private var isWaterMarkImageSelected = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnImagePath.setOnClickListener(this)
- btnAdd.setOnClickListener(this)
+ binding = ActivityAddWaterMarkOnVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@AddWaterMarkOnVideoActivity)
+ btnImagePath.setOnClickListener(this@AddWaterMarkOnVideoActivity)
+ btnAdd.setOnClickListener(this@AddWaterMarkOnVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 13
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnImagePath -> {
- selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ } else {
+ // Fallback for devices below Android 13
+ Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ }
}
R.id.btnAdd -> {
when {
@@ -51,16 +63,16 @@ class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mar
!isWaterMarkImageSelected -> {
Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
}
- TextUtils.isEmpty(edtXPos.text.toString()) -> {
+ TextUtils.isEmpty(binding.edtXPos.text.toString()) -> {
Toast.makeText(this, getString(R.string.x_position_validation), Toast.LENGTH_SHORT).show()
}
- edtXPos.text.toString().toFloat() > 100 || edtXPos.text.toString().toFloat() <= 0 -> {
+ binding.edtXPos.text.toString().toFloat() > 100 || binding.edtXPos.text.toString().toFloat() <= 0 -> {
Toast.makeText(this, getString(R.string.x_validation_invalid), Toast.LENGTH_SHORT).show()
}
- TextUtils.isEmpty(edtYPos.text.toString()) -> {
+ TextUtils.isEmpty(binding.edtYPos.text.toString()) -> {
Toast.makeText(this, getString(R.string.y_position_validation), Toast.LENGTH_SHORT).show()
}
- edtYPos.text.toString().toFloat() > 100 || edtYPos.text.toString().toFloat() <= 0 -> {
+ binding.edtYPos.text.toString().toFloat() > 100 || binding.edtYPos.text.toString().toFloat() <= 0 -> {
Toast.makeText(this, getString(R.string.y_validation_invalid), Toast.LENGTH_SHORT).show()
}
else -> {
@@ -77,11 +89,16 @@ class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mar
when (fileRequestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(binding.tvInputPathVideo.text.toString())
val bit = retriever?.frameAtTime
width = bit?.width
height = bit?.height
@@ -92,7 +109,12 @@ class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mar
}
Common.IMAGE_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathImage.text = mediaFiles[0].path
+ binding.tvInputPathImage.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isWaterMarkImageSelected = true
} else {
Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -104,19 +126,19 @@ class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mar
private fun addWaterMarkProcess() {
val outputPath = getFilePath(this, VIDEO)
val xPos = width?.let {
- (edtXPos.text.toString().toFloat().times(it)).div(100)
+ (binding.edtXPos.text.toString().toFloat().times(it)).div(100)
}
val yPos = height?.let {
- (edtYPos.text.toString().toFloat().times(it)).div(100)
+ (binding.edtYPos.text.toString().toFloat().times(it)).div(100)
}
- val query = ffmpegQueryExtension.addVideoWaterMark(tvInputPathVideo.text.toString(), tvInputPathImage.text.toString(), xPos, yPos, outputPath)
+ val query = ffmpegQueryExtension.addVideoWaterMark(binding.tvInputPathVideo.text.toString(), binding.tvInputPathImage.text.toString(), xPos, yPos, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -131,16 +153,20 @@ class AddWaterMarkOnVideoActivity : BaseActivity(R.layout.activity_add_water_mar
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnImagePath.isEnabled = true
- btnAdd.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnImagePath.isEnabled = true
+ btnAdd.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnImagePath.isEnabled = false
- btnAdd.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnImagePath.isEnabled = false
+ btnAdd.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AspectRatioActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AspectRatioActivity.kt
index 17431e6..420d7d8 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AspectRatioActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AspectRatioActivity.kt
@@ -1,34 +1,44 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityAspectRatioBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.RATIO_1
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_aspect_ratio.btnAspectRatio
-import kotlinx.android.synthetic.main.activity_aspect_ratio.btnVideoPath
-import kotlinx.android.synthetic.main.activity_aspect_ratio.mProgressView
-import kotlinx.android.synthetic.main.activity_aspect_ratio.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_aspect_ratio.tvOutputPath
class AspectRatioActivity : BaseActivity(R.layout.activity_aspect_ratio, R.string.apply_aspect_ratio) {
+ private lateinit var binding: ActivityAspectRatioBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnAspectRatio.setOnClickListener(this)
+ binding = ActivityAspectRatioBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@AspectRatioActivity)
+ btnAspectRatio.setOnClickListener(this@AspectRatioActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 13
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnAspectRatio -> {
when {
@@ -46,15 +56,15 @@ class AspectRatioActivity : BaseActivity(R.layout.activity_aspect_ratio, R.strin
private fun applyRatioProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.applyRatio(tvInputPathVideo.text.toString(), RATIO_1, outputPath)
+ val query = ffmpegQueryExtension.applyRatio(binding.tvInputPathVideo.text.toString(), RATIO_1, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -74,7 +84,11 @@ class AspectRatioActivity : BaseActivity(R.layout.activity_aspect_ratio, R.strin
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -84,15 +98,19 @@ class AspectRatioActivity : BaseActivity(R.layout.activity_aspect_ratio, R.strin
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnAspectRatio.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnAspectRatio.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnAspectRatio.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnAspectRatio.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImageAndVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImageAndVideoActivity.kt
index 596cf15..1d31b8f 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImageAndVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImageAndVideoActivity.kt
@@ -2,44 +2,58 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityMergeImageAndVideoBinding
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.LogMessage
import com.simform.videooperations.Paths
import java.util.concurrent.CompletableFuture.runAsync
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.btnCombine
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.btnImagePath
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.edtSecond
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.mProgressView
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.tvInputPathImage
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.tvOutputPath
class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_and_video, R.string.merge_image_and_video) {
+ private lateinit var binding: ActivityMergeImageAndVideoBinding
private var isInputVideoSelected = false
private var isWaterMarkImageSelected = false
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnImagePath.setOnClickListener(this)
- btnCombine.setOnClickListener(this)
+ binding = ActivityMergeImageAndVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.merge_image_and_video)
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@CombineImageAndVideoActivity)
+ btnImagePath.setOnClickListener(this@CombineImageAndVideoActivity)
+ btnCombine.setOnClickListener(this@CombineImageAndVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnImagePath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ }
}
R.id.btnCombine -> {
when {
@@ -49,7 +63,7 @@ class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_
!isWaterMarkImageSelected -> {
Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
}
- TextUtils.isEmpty(edtSecond.text.toString().trim()) || edtSecond.text.toString().trim().toInt() == 0 -> {
+ TextUtils.isEmpty(binding.edtSecond.text.toString().trim()) || binding.edtSecond.text.toString().trim().toInt() == 0 -> {
Toast.makeText(this, getString(R.string.please_enter_second), Toast.LENGTH_SHORT).show()
}
else -> {
@@ -66,11 +80,16 @@ class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_
when (fileRequestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(binding.tvInputPathVideo.text.toString())
val bit = retriever?.frameAtTime
width = bit?.width
height = bit?.height
@@ -81,7 +100,12 @@ class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_
}
Common.IMAGE_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathImage.text = mediaFiles[0].path
+ binding.tvInputPathImage.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isWaterMarkImageSelected = true
} else {
Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -95,25 +119,25 @@ class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_
val paths = ArrayList()
val videoPaths1 = Paths()
- videoPaths1.filePath = tvInputPathImage.text.toString()
+ videoPaths1.filePath = binding.tvInputPathImage.text.toString()
videoPaths1.isImageFile = true
val videoPaths2 = Paths()
- videoPaths2.filePath = tvInputPathVideo.text.toString()
+ videoPaths2.filePath = binding.tvInputPathVideo.text.toString()
videoPaths2.isImageFile = false
paths.add(videoPaths1)
paths.add(videoPaths2)
- val query = ffmpegQueryExtension.combineImagesAndVideos(paths, width, height, edtSecond.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.combineImagesAndVideos(paths, width, height, binding.edtSecond.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -128,16 +152,20 @@ class CombineImageAndVideoActivity : BaseActivity(R.layout.activity_merge_image_
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnImagePath.isEnabled = true
- btnCombine.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnImagePath.isEnabled = true
+ btnCombine.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnImagePath.isEnabled = false
- btnCombine.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnImagePath.isEnabled = false
+ btnCombine.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImagesActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImagesActivity.kt
index 99cd490..f3cd0ac 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImagesActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImagesActivity.kt
@@ -1,43 +1,56 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCombineImagesBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import com.simform.videooperations.Paths
-import kotlinx.android.synthetic.main.activity_combine_images.btnCombine
-import kotlinx.android.synthetic.main.activity_combine_images.btnImagePath
-import kotlinx.android.synthetic.main.activity_combine_images.edtSecond
-import kotlinx.android.synthetic.main.activity_combine_images.mProgressView
-import kotlinx.android.synthetic.main.activity_combine_images.tvInputPathImage
-import kotlinx.android.synthetic.main.activity_combine_images.tvOutputPath
class CombineImagesActivity : BaseActivity(R.layout.activity_combine_images, R.string.merge_images) {
+ private lateinit var binding: ActivityCombineImagesBinding
private var isImageSelected: Boolean = false
+
override fun initialization() {
- btnImagePath.setOnClickListener(this)
- btnCombine.setOnClickListener(this)
+ binding = ActivityCombineImagesBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ binding.apply {
+ btnImagePath.setOnClickListener(this@CombineImagesActivity)
+ btnCombine.setOnClickListener(this@CombineImagesActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnImagePath -> {
- Common.selectFile(this, maxSelection = 25, isImageSelection = true, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ } else {
+ Common.selectFile(
+ this,
+ maxSelection = 25,
+ isImageSelection = true,
+ isAudioSelection = false
+ )
+ }
}
R.id.btnCombine -> {
when {
!isImageSelected -> {
Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
}
- TextUtils.isEmpty(edtSecond.text.toString().trim()) || edtSecond.text.toString().trim().toInt() == 0 -> {
+ TextUtils.isEmpty(binding.edtSecond.text.toString().trim()) || binding.edtSecond.text.toString().trim().toInt() == 0 -> {
Toast.makeText(this, getString(R.string.please_enter_second), Toast.LENGTH_SHORT).show()
}
else -> {
@@ -55,7 +68,7 @@ class CombineImagesActivity : BaseActivity(R.layout.activity_combine_images, R.s
Common.IMAGE_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
val size: Int = mediaFiles.size
- tvInputPathImage.text = "$size" + (if (size == 1) " Image " else " Images ") + "selected"
+ binding.tvInputPathImage.text = "$size" + (if (size == 1) " Image " else " Images ") + "selected"
isImageSelected = true
} else {
Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -65,15 +78,21 @@ class CombineImagesActivity : BaseActivity(R.layout.activity_combine_images, R.s
}
private fun processStop() {
- btnImagePath.isEnabled = true
- btnCombine.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnImagePath.isEnabled = true
+ btnCombine.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnImagePath.isEnabled = false
- btnCombine.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ binding.apply {
+ btnImagePath.isEnabled = false
+ btnCombine.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
+ }
}
private fun combineImagesProcess() {
@@ -82,20 +101,25 @@ class CombineImagesActivity : BaseActivity(R.layout.activity_combine_images, R.s
mediaFiles?.let {
for (element in it) {
val paths = Paths()
- paths.filePath = element.path
+ paths.filePath =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, element.uri) ?: ""
+ } else {
+ element.path
+ }
paths.isImageFile = true
pathsList.add(paths)
}
- val query = ffmpegQueryExtension.combineImagesAndVideos(pathsList, 640, 480, edtSecond.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.combineImagesAndVideos(pathsList, 640, 480, binding.edtSecond.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineVideosActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineVideosActivity.kt
index 1a8e96b..6a7f8d3 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineVideosActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineVideosActivity.kt
@@ -2,37 +2,44 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCombineVideosBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import com.simform.videooperations.Paths
import java.util.concurrent.CompletableFuture
-import kotlinx.android.synthetic.main.activity_combine_videos.btnCombine
-import kotlinx.android.synthetic.main.activity_combine_videos.btnVideoPath
-import kotlinx.android.synthetic.main.activity_combine_videos.mProgressView
-import kotlinx.android.synthetic.main.activity_combine_videos.tvInputPathImage
-import kotlinx.android.synthetic.main.activity_combine_videos.tvOutputPath
-import kotlinx.android.synthetic.main.activity_merge_image_and_video.tvInputPathVideo
class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.string.merge_videos) {
+ private lateinit var binding: ActivityCombineVideosBinding
private var isVideoSelected: Boolean = false
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnCombine.setOnClickListener(this)
+ binding = ActivityCombineVideosBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@CombineVideosActivity)
+ btnCombine.setOnClickListener(this@CombineVideosActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 5, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 5, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnCombine -> {
when {
@@ -54,11 +61,18 @@ class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.s
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
val size: Int = mediaFiles.size
- tvInputPathImage.text = "$size" + (if (size == 1) " Video " else " Videos ") + "selected"
+ binding.tvInputPathImage.text = "$size" + (if (size == 1) " Video " else " Videos ") + "selected"
+ this.mediaFiles = mediaFiles
isVideoSelected = true
CompletableFuture.runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri) ?: ""
+ } else {
+ mediaFiles[0].path
+ }
+ )
val bit = retriever?.frameAtTime
if (bit != null) {
width = bit.width
@@ -73,15 +87,19 @@ class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.s
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnCombine.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnCombine.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnCombine.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnCombine.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
private fun combineVideosProcess() {
@@ -90,7 +108,13 @@ class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.s
mediaFiles?.let {
for (element in it) {
val paths = Paths()
- paths.filePath = element.path
+ paths.filePath =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, element.uri) ?: ""
+ } else {
+ element.path
+ }
+
paths.isImageFile = false
pathsList.add(paths)
}
@@ -103,11 +127,11 @@ class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.s
)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CompressVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CompressVideoActivity.kt
index 9361390..b763165 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CompressVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CompressVideoActivity.kt
@@ -2,37 +2,50 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCompressVideoBinding
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import java.io.File
import java.util.concurrent.CompletableFuture
-import kotlinx.android.synthetic.main.activity_compress_video.btnCompress
-import kotlinx.android.synthetic.main.activity_compress_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_compress_video.inputFileSize
-import kotlinx.android.synthetic.main.activity_compress_video.mProgressView
-import kotlinx.android.synthetic.main.activity_compress_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_compress_video.tvOutputPath
class CompressVideoActivity : BaseActivity(R.layout.activity_compress_video, R.string.compress_a_video) {
private var isInputVideoSelected: Boolean = false
+ private lateinit var binding: ActivityCompressVideoBinding
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnCompress.setOnClickListener(this)
+ binding = ActivityCompressVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.compress_a_video)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@CompressVideoActivity)
+ btnCompress.setOnClickListener(this@CompressVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ // check if device is 14 or plus
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnCompress -> {
when {
@@ -53,16 +66,21 @@ class CompressVideoActivity : BaseActivity(R.layout.activity_compress_video, R.s
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
+
isInputVideoSelected = true
CompletableFuture.runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(binding.tvInputPathVideo.text.toString())
val bit = retriever?.frameAtTime
width = bit?.width
height = bit?.height
}
- inputFileSize.text = "Input file Size : ${Common.getFileSize(File(tvInputPathVideo.text.toString()))}"
+ binding.inputFileSize.text = "Input file Size : ${Common.getFileSize(File(binding.tvInputPathVideo.text.toString()))}"
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
}
@@ -72,15 +90,15 @@ class CompressVideoActivity : BaseActivity(R.layout.activity_compress_video, R.s
private fun compressProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.compressor(tvInputPathVideo.text.toString(), width, height, outputPath)
+ val query = ffmpegQueryExtension.compressor(binding.tvInputPathVideo.text.toString(), width, height, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
@SuppressLint("SetTextI18n")
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path_with_size), outputPath, Common.getFileSize(File(outputPath)))
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path_with_size), outputPath, Common.getFileSize(File(outputPath)))
processStop()
}
@@ -95,14 +113,18 @@ class CompressVideoActivity : BaseActivity(R.layout.activity_compress_video, R.s
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnCompress.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnCompress.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnCompress.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnCompress.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CutVideoUsingTimeActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CutVideoUsingTimeActivity.kt
index 73e23c7..177820b 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CutVideoUsingTimeActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CutVideoUsingTimeActivity.kt
@@ -1,14 +1,20 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.text.TextUtils
import android.view.View
import android.widget.TextView
import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityCutVideoUsingTimeBinding
import com.simform.videoimageeditor.ikovac.timepickerwithseconds.MyTimePickerDialog
+import com.simform.videoimageeditor.utils.enableEdgeToEdge
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.Common.TIME_FORMAT
@@ -16,49 +22,54 @@ import com.simform.videooperations.Common.VIDEO_FILE_REQUEST_CODE
import com.simform.videooperations.Common.stringForTime
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.LogMessage
+import java.io.File
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.btnConvert
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.btnSelectEndTime
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.btnSelectStartTime
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.btnVideoPath
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.edtEndTime
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.edtStartTime
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.mProgressView
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.tvInputPath
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.tvMaxTime
-import kotlinx.android.synthetic.main.activity_cut_video_using_time.tvOutputPath
class CutVideoUsingTimeActivity : BaseActivity(R.layout.activity_cut_video_using_time, R.string.cut_video_using_time) {
private var startTimeString: String? = null
private var endTimeString: String? = null
private var maxTimeString: String? = null
+ private lateinit var binding: ActivityCutVideoUsingTimeBinding
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnSelectStartTime.setOnClickListener(this)
- btnSelectEndTime.setOnClickListener(this)
- btnConvert.setOnClickListener(this)
+ binding = ActivityCutVideoUsingTimeBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ enableEdgeToEdge(binding.toolbar.root)
+ binding.toolbar.textTitle.text = getString(R.string.cut_video_using_time)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@CutVideoUsingTimeActivity)
+ btnSelectStartTime.setOnClickListener(this@CutVideoUsingTimeActivity)
+ btnSelectEndTime.setOnClickListener(this@CutVideoUsingTimeActivity)
+ btnConvert.setOnClickListener(this@CutVideoUsingTimeActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ // check if device is 14 or plus
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnSelectStartTime -> {
if (!TextUtils.isEmpty(maxTimeString) && !TextUtils.equals(maxTimeString, getString(R.string.zero_time))) {
- selectTime(edtStartTime, true)
+ selectTime(binding.edtStartTime, true)
} else {
Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
}
}
R.id.btnSelectEndTime -> {
if (!TextUtils.isEmpty(maxTimeString) && !TextUtils.equals(maxTimeString, getString(R.string.zero_time))) {
- selectTime(edtEndTime, false)
+ selectTime(binding.edtEndTime, false)
} else {
Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
}
@@ -91,9 +102,21 @@ class CutVideoUsingTimeActivity : BaseActivity(R.layout.activity_cut_video_using
override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
if (requestCode == VIDEO_FILE_REQUEST_CODE) {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPath.text = mediaFiles[0].path
- maxTimeString = stringForTime(mediaFiles[0].duration)
- tvMaxTime.text = "Selected video max time : $maxTimeString"
+ binding.tvInputPath.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
+ maxTimeString =
+ stringForTime(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.getDurationFromFile(File(binding.tvInputPath.text.toString()))
+ } else {
+ mediaFiles[0].duration
+ }
+ )
+ binding.tvMaxTime.text = "Selected video max time : $maxTimeString"
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
}
@@ -149,14 +172,14 @@ class CutVideoUsingTimeActivity : BaseActivity(R.layout.activity_cut_video_using
@SuppressLint("SetTextI18n")
private fun cutProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.cutVideo(tvInputPath.text.toString(), startTimeString, endTimeString, outputPath)
+ val query = ffmpegQueryExtension.cutVideo(binding.tvInputPath.text.toString(), startTimeString, endTimeString, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -171,18 +194,22 @@ class CutVideoUsingTimeActivity : BaseActivity(R.layout.activity_cut_video_using
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnSelectStartTime.isEnabled = true
- btnSelectEndTime.isEnabled = true
- btnConvert.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnSelectStartTime.isEnabled = true
+ btnSelectEndTime.isEnabled = true
+ btnConvert.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnSelectStartTime.isEnabled = false
- btnSelectEndTime.isEnabled = false
- btnConvert.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnSelectStartTime.isEnabled = false
+ btnSelectEndTime.isEnabled = false
+ btnConvert.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractAudioActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractAudioActivity.kt
index 635a322..f543f26 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractAudioActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractAudioActivity.kt
@@ -5,22 +5,25 @@ import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityExtractAudioBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_extract_audio.btnExtract
-import kotlinx.android.synthetic.main.activity_extract_audio.btnVideoPath
-import kotlinx.android.synthetic.main.activity_extract_audio.mProgressView
-import kotlinx.android.synthetic.main.activity_extract_audio.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_extract_audio.tvOutputPath
-class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.string.extract_audio_from_video) {
+class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.string.extract_audio) {
+ private lateinit var binding: ActivityExtractAudioBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnExtract.setOnClickListener(this)
+ binding = ActivityExtractAudioBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@ExtractAudioActivity)
+ btnExtract.setOnClickListener(this@ExtractAudioActivity)
+ }
}
override fun onClick(v: View?) {
@@ -35,24 +38,24 @@ class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.str
}
else -> {
processStart()
- extractProcess()
+ extractAudioProcess()
}
}
}
}
}
- private fun extractProcess() {
+ private fun extractAudioProcess() {
val outputPath = Common.getFilePath(this, Common.MP3)
- val query = ffmpegQueryExtension.extractAudio(tvInputPathVideo.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.extractAudio(binding.tvInputPathVideo.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -70,7 +73,7 @@ class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.str
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = mediaFiles[0].path
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -80,14 +83,18 @@ class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.str
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnExtract.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnExtract.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnExtract.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnExtract.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractImagesActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractImagesActivity.kt
index 9d9c436..a38d59f 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractImagesActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractImagesActivity.kt
@@ -1,34 +1,45 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityExtractImagesBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.Statistics
import java.io.File
-import kotlinx.android.synthetic.main.activity_extract_images.btnExtract
-import kotlinx.android.synthetic.main.activity_extract_images.btnVideoPath
-import kotlinx.android.synthetic.main.activity_extract_images.mProgressView
-import kotlinx.android.synthetic.main.activity_extract_images.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_extract_images.tvOutputPath
class ExtractImagesActivity : BaseActivity(R.layout.activity_extract_images, R.string.extract_frame_from_video) {
+ private lateinit var binding: ActivityExtractImagesBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnExtract.setOnClickListener(this)
+ binding = ActivityExtractImagesBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@ExtractImagesActivity)
+ btnExtract.setOnClickListener(this@ExtractImagesActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnExtract -> {
when {
@@ -49,7 +60,12 @@ class ExtractImagesActivity : BaseActivity(R.layout.activity_extract_images, R.s
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -61,16 +77,16 @@ class ExtractImagesActivity : BaseActivity(R.layout.activity_extract_images, R.s
@SuppressLint("SetTextI18n")
private fun extractProcess() {
val outputPath = Common.getFilePath(this, Common.IMAGE)
- val query = ffmpegQueryExtension.extractImages(tvInputPathVideo.text.toString(), outputPath, spaceOfFrame = 4f)
+ val query = ffmpegQueryExtension.extractImages(binding.tvInputPathVideo.text.toString(), outputPath, spaceOfFrame = 4f)
var totalFramesExtracted = 0
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun statisticsProcess(statistics: Statistics) {
totalFramesExtracted = statistics.videoFrameNumber
- tvOutputPath.text = "Frames : ${statistics.videoFrameNumber}"
+ binding.tvOutputPath.text = "Frames : ${statistics.videoFrameNumber}"
}
override fun success() {
- tvOutputPath.text = "Output Directory : \n${File(getExternalFilesDir(Common.OUT_PUT_DIR).toString()).absolutePath} \n\nTotal Frames Extracted: $totalFramesExtracted"
+ binding.tvOutputPath.text = "Output Directory : \n${outputPath} \n\nTotal Frames Extracted: $totalFramesExtracted"
processStop()
}
@@ -85,14 +101,18 @@ class ExtractImagesActivity : BaseActivity(R.layout.activity_extract_images, R.s
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnExtract.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnExtract.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnExtract.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnExtract.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/FastAndSlowVideoMotionActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/FastAndSlowVideoMotionActivity.kt
index 8d813cf..775f74d 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/FastAndSlowVideoMotionActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/FastAndSlowVideoMotionActivity.kt
@@ -1,33 +1,43 @@
package com.simform.videoimageeditor.videoProcessActivity
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityFastAndSlowVideoMotionBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.btnMotion
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.btnVideoPath
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.mProgressView
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.motionType
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.tvOutputPath
class FastAndSlowVideoMotionActivity : BaseActivity(R.layout.activity_fast_and_slow_video_motion, R.string.fast_slow_motion_video) {
+ private lateinit var binding: ActivityFastAndSlowVideoMotionBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnMotion.setOnClickListener(this)
+ binding = ActivityFastAndSlowVideoMotionBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@FastAndSlowVideoMotionActivity)
+ btnMotion.setOnClickListener(this@FastAndSlowVideoMotionActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnMotion -> {
when {
@@ -47,18 +57,18 @@ class FastAndSlowVideoMotionActivity : BaseActivity(R.layout.activity_fast_and_s
val outputPath = Common.getFilePath(this, Common.VIDEO)
var setpts = 0.5
var atempo = 2.0
- if (!motionType.isChecked) {
+ if (!binding.motionType.isChecked) {
setpts = 2.0
atempo = 0.5
}
- val query = ffmpegQueryExtension.videoMotion(tvInputPathVideo.text.toString(), outputPath, setpts, atempo)
+ val query = ffmpegQueryExtension.videoMotion(binding.tvInputPathVideo.text.toString(), outputPath, setpts, atempo)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -76,7 +86,11 @@ class FastAndSlowVideoMotionActivity : BaseActivity(R.layout.activity_fast_and_s
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -86,14 +100,18 @@ class FastAndSlowVideoMotionActivity : BaseActivity(R.layout.activity_fast_and_s
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnMotion.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnMotion.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnMotion.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnMotion.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ImageToVideoConvertActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ImageToVideoConvertActivity.kt
index c79cfc8..4df2198 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ImageToVideoConvertActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ImageToVideoConvertActivity.kt
@@ -4,9 +4,12 @@ import android.annotation.SuppressLint
import android.text.TextUtils
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityImageToVideoConvertBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
@@ -14,31 +17,37 @@ import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.ISize
import com.simform.videooperations.LogMessage
import com.simform.videooperations.SizeOfImage
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.btnConvert
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.btnImagePath
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.edtSecond
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.mProgressView
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.tvInputPath
-import kotlinx.android.synthetic.main.activity_image_to_video_convert.tvOutputPath
class ImageToVideoConvertActivity : BaseActivity(R.layout.activity_image_to_video_convert, R.string.image_to_video) {
+ private lateinit var binding: ActivityImageToVideoConvertBinding
private var isFileSelected: Boolean = false
+
override fun initialization() {
- btnImagePath.setOnClickListener(this)
- btnConvert.setOnClickListener(this)
+ binding = ActivityImageToVideoConvertBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnImagePath.setOnClickListener(this@ImageToVideoConvertActivity)
+ btnConvert.setOnClickListener(this@ImageToVideoConvertActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnImagePath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
+ }
}
R.id.btnConvert -> {
when {
!isFileSelected -> {
Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
}
- TextUtils.isEmpty(edtSecond.text.toString().trim()) || edtSecond.text.toString().trim().toInt() == 0 -> {
+ TextUtils.isEmpty(binding.edtSecond.text.toString().trim()) || binding.edtSecond.text.toString().trim().toInt() == 0 -> {
Toast.makeText(this, getString(R.string.please_enter_second), Toast.LENGTH_SHORT).show()
}
else -> {
@@ -55,7 +64,12 @@ class ImageToVideoConvertActivity : BaseActivity(R.layout.activity_image_to_vide
if (requestCode == Common.IMAGE_FILE_REQUEST_CODE) {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
isFileSelected = true
- tvInputPath.text = mediaFiles[0].path
+ binding.tvInputPath.text =
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
} else {
isFileSelected = false
Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -65,16 +79,16 @@ class ImageToVideoConvertActivity : BaseActivity(R.layout.activity_image_to_vide
private fun createVideo() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val size: ISize = SizeOfImage(tvInputPath.text.toString())
- val query = ffmpegQueryExtension.imageToVideo(tvInputPath.text.toString(), outputPath, edtSecond.text.toString().toInt(), size.width(), size.height())
+ val size: ISize = SizeOfImage(binding.tvInputPath.text.toString())
+ val query = ffmpegQueryExtension.imageToVideo(binding.tvInputPath.text.toString(), outputPath, binding.edtSecond.text.toString().toInt(), size.width(), size.height())
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -90,14 +104,18 @@ class ImageToVideoConvertActivity : BaseActivity(R.layout.activity_image_to_vide
}
private fun processStop() {
- btnImagePath.isEnabled = true
- btnConvert.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnImagePath.isEnabled = true
+ btnConvert.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnImagePath.isEnabled = false
- btnConvert.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnImagePath.isEnabled = false
+ btnConvert.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeAudioVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeAudioVideoActivity.kt
index c7c5e8d..399d779 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeAudioVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeAudioVideoActivity.kt
@@ -5,26 +5,27 @@ import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityMergeAudioVideoBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_merge_audio_video.btnMerge
-import kotlinx.android.synthetic.main.activity_merge_audio_video.btnMp3Path
-import kotlinx.android.synthetic.main.activity_merge_audio_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_merge_audio_video.mProgressView
-import kotlinx.android.synthetic.main.activity_merge_audio_video.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_merge_audio_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_merge_audio_video.tvOutputPath
class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video, R.string.merge_video_and_audio) {
+ private lateinit var binding: ActivityMergeAudioVideoBinding
private var isInputVideoSelected: Boolean = false
private var isInputAudioSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnMp3Path.setOnClickListener(this)
- btnMerge.setOnClickListener(this)
+ binding = ActivityMergeAudioVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@MergeAudioVideoActivity)
+ btnMp3Path.setOnClickListener(this@MergeAudioVideoActivity)
+ btnMerge.setOnClickListener(this@MergeAudioVideoActivity)
+ }
}
override fun onClick(v: View?) {
@@ -54,15 +55,15 @@ class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video
private fun mergeProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.mergeAudioVideo(tvInputPathVideo.text.toString(), tvInputPathAudio.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.mergeAudioVideo(binding.tvInputPathVideo.text.toString(), binding.tvInputPathAudio.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -81,7 +82,7 @@ class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = mediaFiles[0].path
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -89,7 +90,7 @@ class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video
}
Common.AUDIO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathAudio.text = mediaFiles[0].path
+ binding.tvInputPathAudio.text = mediaFiles[0].path
isInputAudioSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -99,16 +100,20 @@ class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnMp3Path.isEnabled = true
- btnMerge.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnMp3Path.isEnabled = true
+ btnMerge.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnMp3Path.isEnabled = false
- btnMerge.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnMp3Path.isEnabled = false
+ btnMerge.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeImageAndMP3Activity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeImageAndMP3Activity.kt
index 3c9dfaf..c2475b9 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeImageAndMP3Activity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeImageAndMP3Activity.kt
@@ -5,26 +5,26 @@ import android.widget.Toast
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityMergeImageAndMp3Binding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
-import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnImagePath
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnMerge
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnMp3Path
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.mProgressView
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvInputPathAudio
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvInputPathImage
-import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvOutputPath
class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_mp3, R.string.merge_image_and_audio) {
+ private lateinit var binding: ActivityMergeImageAndMp3Binding
private var isInputImageSelected: Boolean = false
private var isInputMP3Selected: Boolean = false
+
override fun initialization() {
- btnImagePath.setOnClickListener(this)
- btnMp3Path.setOnClickListener(this)
- btnMerge.setOnClickListener(this)
+ binding = ActivityMergeImageAndMp3Binding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnImagePath.setOnClickListener(this@MergeImageAndMP3Activity)
+ btnMp3Path.setOnClickListener(this@MergeImageAndMP3Activity)
+ btnMerge.setOnClickListener(this@MergeImageAndMP3Activity)
+ }
}
override fun onClick(v: View?) {
@@ -54,15 +54,15 @@ class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_
private fun mergeProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.mergeImageAndAudio(tvInputPathImage.text.toString(), tvInputPathAudio.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.mergeImageAndAudio(binding.tvInputPathImage.text.toString(), binding.tvInputPathAudio.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -81,7 +81,7 @@ class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_
when (requestCode) {
Common.IMAGE_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathImage.text = mediaFiles[0].path
+ binding.tvInputPathImage.text = mediaFiles[0].path
isInputImageSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -89,7 +89,7 @@ class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_
}
Common.AUDIO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathAudio.text = mediaFiles[0].path
+ binding.tvInputPathAudio.text = mediaFiles[0].path
isInputMP3Selected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -99,16 +99,20 @@ class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_
}
private fun processStop() {
- btnImagePath.isEnabled = true
- btnMp3Path.isEnabled = true
- btnMerge.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnImagePath.isEnabled = true
+ btnMp3Path.isEnabled = true
+ btnMerge.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnImagePath.isEnabled = false
- btnMp3Path.isEnabled = false
- btnMerge.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnImagePath.isEnabled = false
+ btnMp3Path.isEnabled = false
+ btnMerge.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/RemoveAudioFromVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/RemoveAudioFromVideoActivity.kt
index a3146a3..00252c2 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/RemoveAudioFromVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/RemoveAudioFromVideoActivity.kt
@@ -1,33 +1,44 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityRemoveAudioFromVideoBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_remove_audio_from_video.btnRemove
-import kotlinx.android.synthetic.main.activity_remove_audio_from_video.btnVideoPath
-import kotlinx.android.synthetic.main.activity_remove_audio_from_video.mProgressView
-import kotlinx.android.synthetic.main.activity_remove_audio_from_video.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_remove_audio_from_video.tvOutputPath
class RemoveAudioFromVideoActivity : BaseActivity(R.layout.activity_remove_audio_from_video, R.string.audio_remove_from_video) {
+ private lateinit var binding: ActivityRemoveAudioFromVideoBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnRemove.setOnClickListener(this)
+ binding = ActivityRemoveAudioFromVideoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@RemoveAudioFromVideoActivity)
+ btnRemove.setOnClickListener(this@RemoveAudioFromVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnRemove -> {
when {
@@ -45,15 +56,15 @@ class RemoveAudioFromVideoActivity : BaseActivity(R.layout.activity_remove_audio
private fun removeAudioProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.removeAudioFromVideo(tvInputPathVideo.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.removeAudioFromVideo(binding.tvInputPathVideo.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -73,7 +84,11 @@ class RemoveAudioFromVideoActivity : BaseActivity(R.layout.activity_remove_audio
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -83,14 +98,18 @@ class RemoveAudioFromVideoActivity : BaseActivity(R.layout.activity_remove_audio
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnRemove.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnRemove.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnRemove.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnRemove.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ReverseVideoActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ReverseVideoActivity.kt
index 5654239..7363076 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ReverseVideoActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ReverseVideoActivity.kt
@@ -1,33 +1,43 @@
package com.simform.videoimageeditor.videoProcessActivity
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityReverseBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_reverse.btnMotion
-import kotlinx.android.synthetic.main.activity_reverse.btnVideoPath
-import kotlinx.android.synthetic.main.activity_reverse.isWithAudioSwitch
-import kotlinx.android.synthetic.main.activity_reverse.mProgressView
-import kotlinx.android.synthetic.main.activity_reverse.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_reverse.tvOutputPath
class ReverseVideoActivity : BaseActivity(R.layout.activity_reverse, R.string.reverse_video) {
+ private lateinit var binding: ActivityReverseBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnMotion.setOnClickListener(this)
+ binding = ActivityReverseBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@ReverseVideoActivity)
+ btnMotion.setOnClickListener(this@ReverseVideoActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnMotion -> {
when {
@@ -45,15 +55,15 @@ class ReverseVideoActivity : BaseActivity(R.layout.activity_reverse, R.string.re
private fun reverseProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.videoReverse(tvInputPathVideo.text.toString(), isWithAudioSwitch.isChecked, outputPath)
+ val query = ffmpegQueryExtension.videoReverse(binding.tvInputPathVideo.text.toString(), binding.isWithAudioSwitch.isChecked, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -72,7 +82,11 @@ class ReverseVideoActivity : BaseActivity(R.layout.activity_reverse, R.string.re
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -82,14 +96,18 @@ class ReverseVideoActivity : BaseActivity(R.layout.activity_reverse, R.string.re
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnMotion.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnMotion.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnMotion.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnMotion.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoFadeInFadeOutActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoFadeInFadeOutActivity.kt
index f4fe4bc..b367178 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoFadeInFadeOutActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoFadeInFadeOutActivity.kt
@@ -2,11 +2,15 @@ package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
import android.media.MediaMetadataRetriever
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityVideoFadeInFadeOutBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
@@ -14,24 +18,31 @@ import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
-import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.btnApplyFadeInFadeOut
-import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.btnVideoPath
-import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.mProgressView
-import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.tvOutputPath
class VideoFadeInFadeOutActivity : BaseActivity(R.layout.activity_video_fade_in_fade_out, R.string.video_fade_in_and_fade_out) {
+ private lateinit var binding: ActivityVideoFadeInFadeOutBinding
private var isInputVideoSelected: Boolean = false
private var selectedVideoDurationInSecond = 0L
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnApplyFadeInFadeOut.setOnClickListener(this)
+ binding = ActivityVideoFadeInFadeOutBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@VideoFadeInFadeOutActivity)
+ btnApplyFadeInFadeOut.setOnClickListener(this@VideoFadeInFadeOutActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnApplyFadeInFadeOut -> {
when {
@@ -49,15 +60,15 @@ class VideoFadeInFadeOutActivity : BaseActivity(R.layout.activity_video_fade_in_
private fun fadeInFadeOutProcess() {
val outputPath = Common.getFilePath(this, Common.VIDEO)
- val query = ffmpegQueryExtension.videoFadeInFadeOut(tvInputPathVideo.text.toString(), selectedVideoDurationInSecond, fadeInEndSeconds = 3, fadeOutStartSeconds = 3, output = outputPath)
+ val query = ffmpegQueryExtension.videoFadeInFadeOut(binding.tvInputPathVideo.text.toString(), selectedVideoDurationInSecond, fadeInEndSeconds = 3, fadeOutStartSeconds = 3, output = outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -77,11 +88,15 @@ class VideoFadeInFadeOutActivity : BaseActivity(R.layout.activity_video_fade_in_
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
CompletableFuture.runAsync {
retriever = MediaMetadataRetriever()
- retriever?.setDataSource(tvInputPathVideo.text.toString())
+ retriever?.setDataSource(binding.tvInputPathVideo.text.toString())
val time = retriever?.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
time?.toLong()?.let {
selectedVideoDurationInSecond = TimeUnit.MILLISECONDS.toSeconds(it)
@@ -95,14 +110,18 @@ class VideoFadeInFadeOutActivity : BaseActivity(R.layout.activity_video_fade_in_
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnApplyFadeInFadeOut.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnApplyFadeInFadeOut.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnApplyFadeInFadeOut.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnApplyFadeInFadeOut.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoRotateFlipActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoRotateFlipActivity.kt
index 8c02811..d24ae71 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoRotateFlipActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoRotateFlipActivity.kt
@@ -1,45 +1,49 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityVideoRotateFlipBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btn90Clockwise
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btn90ClockwiseVerticalFlip
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btn90CounterClockwise
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btn90CounterClockwiseVerticalFlip
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btnRotate180
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btnRotate270
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btnRotate90
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.btnVideoPath
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.mProgressView
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_video_rotate_flip.tvOutputPath
class VideoRotateFlipActivity : BaseActivity(R.layout.activity_video_rotate_flip, R.string.video_rotate) {
+ private lateinit var binding: ActivityVideoRotateFlipBinding
private var isInputVideoSelected: Boolean = false
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnRotate90.setOnClickListener(this)
- btnRotate180.setOnClickListener(this)
- btnRotate270.setOnClickListener(this)
- btn90CounterClockwiseVerticalFlip.setOnClickListener(this)
- btn90Clockwise.setOnClickListener(this)
- btn90CounterClockwise.setOnClickListener(this)
- btn90ClockwiseVerticalFlip.setOnClickListener(this)
+ binding = ActivityVideoRotateFlipBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@VideoRotateFlipActivity)
+ btnRotate90.setOnClickListener(this@VideoRotateFlipActivity)
+ btnRotate180.setOnClickListener(this@VideoRotateFlipActivity)
+ btnRotate270.setOnClickListener(this@VideoRotateFlipActivity)
+ btn90CounterClockwiseVerticalFlip.setOnClickListener(this@VideoRotateFlipActivity)
+ btn90Clockwise.setOnClickListener(this@VideoRotateFlipActivity)
+ btn90CounterClockwise.setOnClickListener(this@VideoRotateFlipActivity)
+ btn90ClockwiseVerticalFlip.setOnClickListener(this@VideoRotateFlipActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnRotate90 -> {
rotateDegree(90, true)
@@ -80,18 +84,18 @@ class VideoRotateFlipActivity : BaseActivity(R.layout.activity_video_rotate_flip
private fun rotateProcess(degree: Int, isRotate: Boolean) {
val outputPath = Common.getFilePath(this, Common.VIDEO)
val query = if (isRotate) {
- ffmpegQueryExtension.rotateVideo(tvInputPathVideo.text.toString(), degree, outputPath)
+ ffmpegQueryExtension.rotateVideo(binding.tvInputPathVideo.text.toString(), degree, outputPath)
} else {
- ffmpegQueryExtension.flipVideo(tvInputPathVideo.text.toString(), degree, outputPath)
+ ffmpegQueryExtension.flipVideo(binding.tvInputPathVideo.text.toString(), degree, outputPath)
}
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -111,7 +115,11 @@ class VideoRotateFlipActivity : BaseActivity(R.layout.activity_video_rotate_flip
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -121,18 +129,22 @@ class VideoRotateFlipActivity : BaseActivity(R.layout.activity_video_rotate_flip
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnRotate90.isEnabled = true
- btnRotate180.isEnabled = true
- btnRotate270.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnRotate90.isEnabled = true
+ btnRotate180.isEnabled = true
+ btnRotate270.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnRotate90.isEnabled = false
- btnRotate180.isEnabled = false
- btnRotate270.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnRotate90.isEnabled = false
+ btnRotate180.isEnabled = false
+ btnRotate270.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoToGifActivity.kt b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoToGifActivity.kt
index fcf4850..1518bd9 100644
--- a/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoToGifActivity.kt
+++ b/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoToGifActivity.kt
@@ -1,34 +1,45 @@
package com.simform.videoimageeditor.videoProcessActivity
import android.annotation.SuppressLint
+import android.os.Build
import android.view.View
import android.widget.Toast
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import com.jaiselrahman.filepicker.model.MediaFile
import com.simform.videoimageeditor.BaseActivity
import com.simform.videoimageeditor.R
+import com.simform.videoimageeditor.databinding.ActivityVideoToGifBinding
import com.simform.videooperations.CallBackOfQuery
import com.simform.videooperations.Common
import com.simform.videooperations.FFmpegCallBack
import com.simform.videooperations.FFmpegQueryExtension
import com.simform.videooperations.LogMessage
import java.util.concurrent.CyclicBarrier
-import kotlinx.android.synthetic.main.activity_video_to_gif.btnConvertIntoGif
-import kotlinx.android.synthetic.main.activity_video_to_gif.btnVideoPath
-import kotlinx.android.synthetic.main.activity_video_to_gif.mProgressView
-import kotlinx.android.synthetic.main.activity_video_to_gif.tvInputPathVideo
-import kotlinx.android.synthetic.main.activity_video_to_gif.tvOutputPath
class VideoToGifActivity : BaseActivity(R.layout.activity_video_to_gif, R.string.video_to_gif) {
private var isInputVideoSelected: Boolean = false
+ private lateinit var binding: ActivityVideoToGifBinding
+
override fun initialization() {
- btnVideoPath.setOnClickListener(this)
- btnConvertIntoGif.setOnClickListener(this)
+ binding = ActivityVideoToGifBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.apply {
+ btnVideoPath.setOnClickListener(this@VideoToGifActivity)
+ btnConvertIntoGif.setOnClickListener(this@VideoToGifActivity)
+ }
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnVideoPath -> {
- Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickSingleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))
+ } else {
+ // Fallback for devices below Android 14
+ Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
+ }
}
R.id.btnConvertIntoGif -> {
when {
@@ -46,15 +57,15 @@ class VideoToGifActivity : BaseActivity(R.layout.activity_video_to_gif, R.string
private fun convertProcess() {
val outputPath = Common.getFilePath(this, Common.GIF)
- val query = ffmpegQueryExtension.convertVideoToGIF(tvInputPathVideo.text.toString(), outputPath)
+ val query = ffmpegQueryExtension.convertVideoToGIF(binding.tvInputPathVideo.text.toString(), outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
override fun process(logMessage: LogMessage) {
- tvOutputPath.text = logMessage.text
+ binding.tvOutputPath.text = logMessage.text
}
override fun success() {
- tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
+ binding.tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
processStop()
}
@@ -74,7 +85,11 @@ class VideoToGifActivity : BaseActivity(R.layout.activity_video_to_gif, R.string
when (requestCode) {
Common.VIDEO_FILE_REQUEST_CODE -> {
if (mediaFiles != null && mediaFiles.isNotEmpty()) {
- tvInputPathVideo.text = mediaFiles[0].path
+ binding.tvInputPathVideo.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Common.saveFileToTempAndGetPath(this, mediaFiles[0].uri)
+ } else {
+ mediaFiles[0].path
+ }
isInputVideoSelected = true
} else {
Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
@@ -84,14 +99,18 @@ class VideoToGifActivity : BaseActivity(R.layout.activity_video_to_gif, R.string
}
private fun processStop() {
- btnVideoPath.isEnabled = true
- btnConvertIntoGif.isEnabled = true
- mProgressView.visibility = View.GONE
+ binding.apply {
+ btnVideoPath.isEnabled = true
+ btnConvertIntoGif.isEnabled = true
+ mProgressView.root.visibility = View.GONE
+ }
}
private fun processStart() {
- btnVideoPath.isEnabled = false
- btnConvertIntoGif.isEnabled = false
- mProgressView.visibility = View.VISIBLE
+ binding.apply {
+ btnVideoPath.isEnabled = false
+ btnConvertIntoGif.isEnabled = false
+ mProgressView.root.visibility = View.VISIBLE
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 0000000..4388a7d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_add_text_on_video.xml b/app/src/main/res/layout/activity_add_text_on_video.xml
index 636328d..dd2e982 100644
--- a/app/src/main/res/layout/activity_add_text_on_video.xml
+++ b/app/src/main/res/layout/activity_add_text_on_video.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddTextOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_add_water_mark_on_video.xml b/app/src/main/res/layout/activity_add_water_mark_on_video.xml
index aee695d..17c153c 100644
--- a/app/src/main/res/layout/activity_add_water_mark_on_video.xml
+++ b/app/src/main/res/layout/activity_add_water_mark_on_video.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_aspect_ratio.xml b/app/src/main/res/layout/activity_aspect_ratio.xml
index 60d968e..52e1bbf 100644
--- a/app/src/main/res/layout/activity_aspect_ratio.xml
+++ b/app/src/main/res/layout/activity_aspect_ratio.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_audios_merge.xml b/app/src/main/res/layout/activity_audios_merge.xml
index c1af1c2..2a1609f 100644
--- a/app/src/main/res/layout/activity_audios_merge.xml
+++ b/app/src/main/res/layout/activity_audios_merge.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddTextOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_change_audio_valume.xml b/app/src/main/res/layout/activity_change_audio_valume.xml
index 24eaf18..fcba5b8 100644
--- a/app/src/main/res/layout/activity_change_audio_valume.xml
+++ b/app/src/main/res/layout/activity_change_audio_valume.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddTextOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_combine_images.xml b/app/src/main/res/layout/activity_combine_images.xml
index dcc65ea..c98de32 100644
--- a/app/src/main/res/layout/activity_combine_images.xml
+++ b/app/src/main/res/layout/activity_combine_images.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.CombineImagesActivity">
+
+
diff --git a/app/src/main/res/layout/activity_combine_videos.xml b/app/src/main/res/layout/activity_combine_videos.xml
index 499732e..b31b17d 100644
--- a/app/src/main/res/layout/activity_combine_videos.xml
+++ b/app/src/main/res/layout/activity_combine_videos.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.CombineImagesActivity">
+
+
diff --git a/app/src/main/res/layout/activity_compress_audio.xml b/app/src/main/res/layout/activity_compress_audio.xml
index 9755f0b..e091eaf 100644
--- a/app/src/main/res/layout/activity_compress_audio.xml
+++ b/app/src/main/res/layout/activity_compress_audio.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddTextOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_compress_video.xml b/app/src/main/res/layout/activity_compress_video.xml
index 8bbe555..188bee5 100644
--- a/app/src/main/res/layout/activity_compress_video.xml
+++ b/app/src/main/res/layout/activity_compress_video.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_crop_audio.xml b/app/src/main/res/layout/activity_crop_audio.xml
index da43905..e137518 100644
--- a/app/src/main/res/layout/activity_crop_audio.xml
+++ b/app/src/main/res/layout/activity_crop_audio.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.CutVideoUsingTimeActivity">
+
+
diff --git a/app/src/main/res/layout/activity_cut_video_using_time.xml b/app/src/main/res/layout/activity_cut_video_using_time.xml
index 47955a5..d3e672f 100644
--- a/app/src/main/res/layout/activity_cut_video_using_time.xml
+++ b/app/src/main/res/layout/activity_cut_video_using_time.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.CutVideoUsingTimeActivity">
+
+
diff --git a/app/src/main/res/layout/activity_extract_audio.xml b/app/src/main/res/layout/activity_extract_audio.xml
index 304ecfa..80462d9 100644
--- a/app/src/main/res/layout/activity_extract_audio.xml
+++ b/app/src/main/res/layout/activity_extract_audio.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_extract_images.xml b/app/src/main/res/layout/activity_extract_images.xml
index 245f20c..6f687c9 100644
--- a/app/src/main/res/layout/activity_extract_images.xml
+++ b/app/src/main/res/layout/activity_extract_images.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_fast_and_slow_audio.xml b/app/src/main/res/layout/activity_fast_and_slow_audio.xml
index af6d753..f439ca8 100644
--- a/app/src/main/res/layout/activity_fast_and_slow_audio.xml
+++ b/app/src/main/res/layout/activity_fast_and_slow_audio.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".otherFFMPEGProcessActivity.FastAndSlowAudioActivity">
+
+
diff --git a/app/src/main/res/layout/activity_fast_and_slow_video_motion.xml b/app/src/main/res/layout/activity_fast_and_slow_video_motion.xml
index d80fc63..0969178 100644
--- a/app/src/main/res/layout/activity_fast_and_slow_video_motion.xml
+++ b/app/src/main/res/layout/activity_fast_and_slow_video_motion.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.AddWaterMarkOnVideoActivity">
+
+
diff --git a/app/src/main/res/layout/activity_image_to_video_convert.xml b/app/src/main/res/layout/activity_image_to_video_convert.xml
index ecfbec0..ff6ecf6 100644
--- a/app/src/main/res/layout/activity_image_to_video_convert.xml
+++ b/app/src/main/res/layout/activity_image_to_video_convert.xml
@@ -6,6 +6,12 @@
android:layout_height="match_parent"
tools:context=".videoProcessActivity.ImageToVideoConvertActivity">
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 0ae63af..f087d38 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -7,6 +7,12 @@
android:orientation="vertical"
tools:context=".MainActivity">
+
+
+ app:layout_constraintTop_toBottomOf="@id/toolbar"/>