Feature: add compose ui

pull/209/head
Lonely dreamers 3 years ago
parent ced1012f43
commit 2a71427dfb
  1. 2
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/AudioTrackController.kt
  2. 4
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaMetadataController.kt
  3. 4
      AndroidMedia/src/main/java/com/frank/androidmedia/util/WavUtil.kt
  4. 44
      app/build.gradle
  5. 30
      app/src/main/AndroidManifest.xml
  6. 2
      app/src/main/java/com/frank/ffmpeg/activity/AudioPlayActivity.kt
  7. 43
      app/src/main/java/com/frank/ffmpeg/activity/ComposeActivity.kt
  8. 4
      app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.kt
  9. 1
      app/src/main/java/com/frank/ffmpeg/activity/LiveActivity.kt
  10. 6
      app/src/main/java/com/frank/ffmpeg/activity/MainActivity.kt
  11. 8
      app/src/main/java/com/frank/ffmpeg/activity/ui/theme/Color.kt
  12. 11
      app/src/main/java/com/frank/ffmpeg/activity/ui/theme/Shape.kt
  13. 47
      app/src/main/java/com/frank/ffmpeg/activity/ui/theme/Theme.kt
  14. 28
      app/src/main/java/com/frank/ffmpeg/activity/ui/theme/Type.kt
  15. 1
      app/src/main/res/values-en/strings.xml
  16. 1
      app/src/main/res/values/strings.xml
  17. 11
      build.gradle
  18. 2
      gradle.properties
  19. 2
      libmp3/src/main/java/com/frank/mp3/Mp3Converter.kt

@ -49,7 +49,7 @@ open class AudioTrackController {
private fun initMediaCodec(mediaFormat: MediaFormat): Boolean { private fun initMediaCodec(mediaFormat: MediaFormat): Boolean {
val mimeType = mediaFormat.getString(MediaFormat.KEY_MIME) val mimeType = mediaFormat.getString(MediaFormat.KEY_MIME)
mediaCodec = MediaCodec.createDecoderByType(mimeType) mediaCodec = MediaCodec.createDecoderByType(mimeType.toString())
return try { return try {
mediaCodec!!.configure(mediaFormat, null, null, 0) mediaCodec!!.configure(mediaFormat, null, null, 0)
mediaCodec!!.start() mediaCodec!!.start()

@ -57,7 +57,9 @@ open class MediaMetadataController {
try { try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
val frameRateStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE) val frameRateStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE)
if (frameRateStr != null) {
frameRate = frameRateStr.toFloat() frameRate = frameRateStr.toFloat()
}
Log.i(TAG, "frameRate=$frameRate") Log.i(TAG, "frameRate=$frameRate")
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -69,8 +71,10 @@ open class MediaMetadataController {
thumbnail = retriever.getFrameAtTime(0) thumbnail = retriever.getFrameAtTime(0)
} else if (hasAudioStr != null && "yes" == hasAudioStr) { } else if (hasAudioStr != null && "yes" == hasAudioStr) {
val byteArray = retriever.embeddedPicture val byteArray = retriever.embeddedPicture
if (byteArray != null) {
thumbnail = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, null) thumbnail = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, null)
} }
}
if (thumbnail != null) { if (thumbnail != null) {
Log.i(TAG, "thumbnail width=${thumbnail?.width}, height=${thumbnail?.height}") Log.i(TAG, "thumbnail width=${thumbnail?.width}, height=${thumbnail?.height}")
} }

@ -31,7 +31,7 @@ object WavUtil {
val h: ByteArray = try { val h: ByteArray = try {
header.header header.header
} catch (e1: IOException) { } catch (e1: IOException) {
Log.e("WavUtil", e1.message) Log.e("WavUtil", e1.message.toString())
return false return false
} }
if (h.size != 44) return false if (h.size != 44) return false
@ -52,7 +52,7 @@ object WavUtil {
inStream.close() inStream.close()
ouStream.close() ouStream.close()
} catch (e: IOException) { } catch (e: IOException) {
Log.e("WavUtil", e.message) Log.e("WavUtil", e.message.toString())
return false return false
} }
if (deletePcmFile) { if (deletePcmFile) {

@ -20,6 +20,9 @@ android {
ndk { ndk {
abiFilters "armeabi-v7a", "arm64-v8a" abiFilters "armeabi-v7a", "arm64-v8a"
} }
vectorDrawables {
useSupportLibrary true
}
} }
buildTypes { buildTypes {
release { release {
@ -51,6 +54,21 @@ android {
pickFirst 'lib/arm64-v8a/libmp3lame.so' pickFirst 'lib/arm64-v8a/libmp3lame.so'
pickFirst 'lib/armeabi-v7a/libffmpeg.so' pickFirst 'lib/armeabi-v7a/libffmpeg.so'
pickFirst 'lib/arm64-v8a/libffmpeg.so' pickFirst 'lib/arm64-v8a/libffmpeg.so'
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
} }
} }
@ -65,4 +83,30 @@ dependencies {
implementation project(':AndroidMedia') implementation project(':AndroidMedia')
//implementation "libmp3" if you need mp3-lite module //implementation "libmp3" if you need mp3-lite module
// implementation project(':libmp3') // implementation project(':libmp3')
// compose start
implementation 'androidx.compose.ui:ui:1.1.1'
// Tooling support (Previews, etc.)
implementation 'androidx.compose.ui:ui-tooling:1.1.1'
// Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
implementation 'androidx.compose.foundation:foundation:1.1.1'
// Material Design
implementation 'androidx.compose.material:material:1.1.1'
// Material design icons
implementation 'androidx.compose.material:material-icons-core:1.1.1'
implementation 'androidx.compose.material:material-icons-extended:1.1.1'
// Integration with activities
implementation 'androidx.activity:activity-compose:1.4.0'
// Integration with ViewModels
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1'
// Integration with observables
implementation 'androidx.compose.runtime:runtime-livedata:1.1.1'
implementation 'androidx.compose.runtime:runtime-rxjava2:1.1.1'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
// UI Tests
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
// compose end
} }

@ -3,17 +3,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.frank.ffmpeg"> package="com.frank.ffmpeg">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application <application
android:name=".FFmpegApplication" android:name=".FFmpegApplication"
android:fullBackupOnly="true" android:fullBackupOnly="true"
@ -22,8 +11,12 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:replace="android:name"> tools:replace="android:name">
<activity
<activity android:name=".activity.MainActivity"> android:name=".activity.ComposeActivity"
android:exported="false"
android:theme="@style/AppTheme" />
<activity android:name=".activity.MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -47,5 +40,16 @@
<activity android:name=".activity.AudioPlayActivity" /> <activity android:name=".activity.AudioPlayActivity" />
<activity android:name=".activity.EqualizerActivity" /> <activity android:name=".activity.EqualizerActivity" />
</application> </application>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
</manifest> </manifest>

@ -48,7 +48,7 @@ class AudioPlayActivity : AppCompatActivity() {
private val mHandler: Handler = @SuppressLint("HandlerLeak") private val mHandler: Handler = @SuppressLint("HandlerLeak")
object : Handler() { object : Handler() {
override fun handleMessage(msg: Message?) { override fun handleMessage(msg: Message) {
super.handleMessage(msg) super.handleMessage(msg)
when (msg?.what) { when (msg?.what) {
MSG_TIME -> { MSG_TIME -> {

@ -0,0 +1,43 @@
package com.frank.ffmpeg.activity
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.frank.ffmpeg.activity.ui.theme.FFmpegAndroidTheme
class ComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
FFmpegAndroidTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
FFmpegAndroidTheme {
Greeting("Android")
}
}

@ -87,7 +87,7 @@ class FilterActivity : BaseActivity(), SurfaceHolder.Callback {
registerLister() registerLister()
hideRunnable = HideRunnable() hideRunnable = HideRunnable()
mHandler.postDelayed(hideRunnable, DELAY_TIME.toLong()) mHandler.postDelayed(hideRunnable!!, DELAY_TIME.toLong())
} }
private fun initView() { private fun initView() {
@ -126,7 +126,7 @@ class FilterActivity : BaseActivity(), SurfaceHolder.Callback {
btnSelect!!.visibility = View.VISIBLE btnSelect!!.visibility = View.VISIBLE
btnSound!!.visibility = View.VISIBLE btnSound!!.visibility = View.VISIBLE
recyclerView!!.visibility = View.VISIBLE recyclerView!!.visibility = View.VISIBLE
mHandler.postDelayed(hideRunnable, DELAY_TIME.toLong()) hideRunnable?.let { it1 -> mHandler.postDelayed(it1, DELAY_TIME.toLong()) }
} }
btnSound!!.setOnCheckedChangeListener { buttonView, isChecked -> setPlayAudio() } btnSound!!.setOnCheckedChangeListener { buttonView, isChecked -> setPlayAudio() }

@ -16,6 +16,7 @@ import android.view.View
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.Toast import android.widget.Toast
import android.widget.ToggleButton import android.widget.ToggleButton
import androidx.compose.runtime.Composable
import com.frank.ffmpeg.R import com.frank.ffmpeg.R
import com.frank.ffmpeg.handler.ConnectionReceiver import com.frank.ffmpeg.handler.ConnectionReceiver

@ -35,7 +35,9 @@ class MainActivity : BaseActivity() {
getString(R.string.video_filter), getString(R.string.video_filter),
getString(R.string.video_preview), getString(R.string.video_preview),
getString(R.string.media_probe), getString(R.string.media_probe),
getString(R.string.audio_effect)) getString(R.string.audio_effect),
getString(R.string.compose)
)
val viewWaterfall: RecyclerView = findViewById(R.id.list_main_item) val viewWaterfall: RecyclerView = findViewById(R.id.list_main_item)
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
@ -71,6 +73,8 @@ class MainActivity : BaseActivity() {
-> intent.setClass(this@MainActivity, ProbeFormatActivity::class.java) -> intent.setClass(this@MainActivity, ProbeFormatActivity::class.java)
8 //audio effect 8 //audio effect
-> intent.setClass(this@MainActivity, AudioEffectActivity::class.java) -> intent.setClass(this@MainActivity, AudioEffectActivity::class.java)
9
-> intent.setClass(this@MainActivity, ComposeActivity::class.java)
else -> { else -> {
} }
} }

@ -0,0 +1,8 @@
package com.frank.ffmpeg.activity.ui.theme
import androidx.compose.ui.graphics.Color
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)

@ -0,0 +1,11 @@
package com.frank.ffmpeg.activity.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)

@ -0,0 +1,47 @@
package com.frank.ffmpeg.activity.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
/* Other default colors to override
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
*/
)
@Composable
fun FFmpegAndroidTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}

@ -0,0 +1,28 @@
package com.frank.ffmpeg.activity.ui.theme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
/* Other default text styles to override
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
)
*/
)

@ -1,5 +1,6 @@
<resources> <resources>
<string name="app_name">FFmpegAndroid</string> <string name="app_name">FFmpegAndroid</string>
<string name="compose">compose</string>
<string name="audio_transform">Audio transform</string> <string name="audio_transform">Audio transform</string>
<string name="audio_cut">Audio cut</string> <string name="audio_cut">Audio cut</string>
<string name="audio_concat">Audio concat</string> <string name="audio_concat">Audio concat</string>

@ -1,5 +1,6 @@
<resources> <resources>
<string name="app_name">FFmpegAndroid</string> <string name="app_name">FFmpegAndroid</string>
<string name="compose">compose</string>
<string name="audio_transform">音频转码</string> <string name="audio_transform">音频转码</string>
<string name="audio_cut">音频剪切</string> <string name="audio_cut">音频剪切</string>
<string name="audio_concat">音频拼接</string> <string name="audio_concat">音频拼接</string>

@ -1,6 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext {
compose_version = '1.0.1'
}
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.3.50'
repositories { repositories {
google() google()
@ -8,7 +11,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.1.3' classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
@ -26,10 +29,10 @@ task clean(type: Delete) {
} }
ext { ext {
compileSdkVersion = 28 compileSdkVersion = 31
buildToolsVersion = '28.0.2' buildToolsVersion = '31.0.2'
minSdkVersion = 21 minSdkVersion = 21
targetSdkVersion = 28 targetSdkVersion = 31
versionCode = 103000 versionCode = 103000
versionName = "1.3.0" versionName = "1.3.0"
} }

@ -15,7 +15,7 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
org.gradle.jvmargs=-Xmx512m org.gradle.jvmargs=-Xmx4096m
android.enableJetifier=true android.enableJetifier=true
android.useAndroidX=true android.useAndroidX=true

@ -227,6 +227,7 @@ open class Mp3Converter {
Log.i(TAG, "prepareDecode get mMediaFormat=$mMediaFormat") Log.i(TAG, "prepareDecode get mMediaFormat=$mMediaFormat")
val mime = mMediaFormat.getString(MediaFormat.KEY_MIME) val mime = mMediaFormat.getString(MediaFormat.KEY_MIME)
if (mime != null) {
if (mime.startsWith("audio")) { if (mime.startsWith("audio")) {
mMediaCodec = MediaCodec.createDecoderByType(mime) mMediaCodec = MediaCodec.createDecoderByType(mime)
mMediaCodec!!.configure(mMediaFormat, null, null, 0) mMediaCodec!!.configure(mMediaFormat, null, null, 0)
@ -236,6 +237,7 @@ open class Mp3Converter {
break break
} }
} }
}
mMediaCodec!!.start() mMediaCodec!!.start()
bufferInfo = BufferInfo() bufferInfo = BufferInfo()

Loading…
Cancel
Save