1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-06 00:02:48 +02:00

Compare commits

...

1 Commits

Author SHA1 Message Date
Benoit Marty
bb84bba92b Try to reduce APK for API29+ 2022-01-19 11:29:22 +01:00
9 changed files with 100 additions and 97 deletions

View File

@@ -116,8 +116,6 @@ android {
defaultConfig {
applicationId "im.vector.app"
// Set to API 21: see #405
minSdk versions.minSdk
targetSdk versions.targetSdk
multiDexEnabled true
@@ -254,7 +252,7 @@ android {
}
}
flavorDimensions "store"
flavorDimensions "store", "api"
productFlavors {
gplay {
@@ -283,6 +281,18 @@ android {
buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\""
buildConfigField "String", "FLAVOR_DESCRIPTION", "\"FDroid\""
}
api21 {
dimension "api"
// Set to API 21: see #405
minSdk versions.minSdk
maxSdk 28
}
api29 {
dimension "api"
minSdk 29
}
}
lintOptions {
@@ -419,7 +429,7 @@ dependencies {
implementation 'com.nulab-inc:zxcvbn:1.5.2'
// To convert voice message on old platforms
implementation 'com.arthenica:ffmpeg-kit-audio:4.5.LTS'
api21Implementation 'com.arthenica:ffmpeg-kit-audio:4.5.LTS'
// Alerter
implementation 'com.github.tapadoo:alerter:7.2.4'

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 New Vector Ltd
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,8 +25,9 @@ import com.arthenica.ffmpegkit.ReturnCode
import im.vector.app.BuildConfig
import timber.log.Timber
import java.io.File
import javax.inject.Inject
class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") {
class DefaultVoiceRecorder @Inject constructor(context: Context) : AbstractVoiceRecorder(context, "mp4") {
override fun setOutputFormat(mediaRecorder: MediaRecorder) {
// Use AAC/MP4 format here
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.voice
import android.content.Context
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import timber.log.Timber
import java.io.File
import javax.inject.Inject
class VoicePlayerHelper @Inject constructor(
context: Context
) {
private val outputDirectory: File by lazy {
File(context.cacheDir, "voice_records").also {
it.mkdirs()
}
}
/**
* Ensure the file is encoded using aac audio codec
*/
fun convertFile(file: File): File {
// Convert to mp4
val targetFile = File(outputDirectory, "Voice.mp4")
if (targetFile.exists()) {
targetFile.delete()
}
val start = System.currentTimeMillis()
val session = FFmpegKit.execute("-i \"${file.path}\" -c:a aac \"${targetFile.path}\"")
val duration = System.currentTimeMillis() - start
Timber.d("Convert to mp4 in $duration ms. Size in bytes from ${file.length()} to ${targetFile.length()}")
return when {
ReturnCode.isSuccess(session.returnCode) -> {
// SUCCESS
targetFile
}
ReturnCode.isCancel(session.returnCode) -> {
// CANCEL
// Fallback to the original file in this case and let the player fail for us
file
}
else -> {
// FAILURE
Timber.e("Command failed with state ${session.state} and rc ${session.returnCode}.${session.failStackTrace}")
// Fallback to the original file in this case and let the player fail for us
file
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 New Vector Ltd
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,9 +21,10 @@ import android.media.MediaRecorder
import android.os.Build
import androidx.annotation.RequiresApi
import java.io.File
import javax.inject.Inject
@RequiresApi(Build.VERSION_CODES.Q)
class VoiceRecorderQ(context: Context) : AbstractVoiceRecorder(context, "ogg") {
class DefaultVoiceRecorder @Inject constructor(context: Context) : AbstractVoiceRecorder(context, "ogg") {
override fun setOutputFormat(mediaRecorder: MediaRecorder) {
// We can directly use OGG here
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.OGG)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 New Vector Ltd
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,18 +16,12 @@
package im.vector.app.features.voice
import android.content.Context
import android.os.Build
import java.io.File
import javax.inject.Inject
class VoiceRecorderProvider @Inject constructor(
private val context: Context
) {
fun provideVoiceRecorder(): VoiceRecorder {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
VoiceRecorderQ(context)
} else {
VoiceRecorderL(context)
}
}
class VoicePlayerHelper @Inject constructor() {
/**
* No op
*/
fun convertFile(file: File) = file
}

View File

@@ -43,6 +43,8 @@ import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.SharedPrefPinCodeStore
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
import im.vector.app.features.ui.UiStateRepository
import im.vector.app.features.voice.DefaultVoiceRecorder
import im.vector.app.features.voice.VoiceRecorder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -81,6 +83,9 @@ abstract class VectorBindModule {
@Binds
abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify
@Binds
abstract fun bindVoiceRecorder(recorder: DefaultVoiceRecorder): VoiceRecorder
}
@InstallIn(SingletonComponent::class)

View File

@@ -738,8 +738,7 @@ class MessageComposerViewModel @AssistedInject constructor(
try {
// Download can fail
val audioFile = session.fileService().downloadFile(action.messageAudioContent)
// Conversion can fail, fallback to the original file in this case and let the player fail for us
val convertedFile = voicePlayerHelper.convertFile(audioFile) ?: audioFile
val convertedFile = voicePlayerHelper.convertFile(audioFile)
// Play can fail
voiceMessageHelper.startOrPausePlayback(action.eventId, convertedFile)
} catch (failure: Throwable) {

View File

@@ -24,7 +24,6 @@ import im.vector.app.BuildConfig
import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker
import im.vector.app.features.voice.VoiceFailure
import im.vector.app.features.voice.VoiceRecorder
import im.vector.app.features.voice.VoiceRecorderProvider
import im.vector.lib.core.utils.timer.CountUpTimer
import im.vector.lib.multipicker.entity.MultiPickerAudioType
import im.vector.lib.multipicker.utils.toMultiPickerAudioType
@@ -43,10 +42,9 @@ import javax.inject.Inject
class VoiceMessageHelper @Inject constructor(
private val context: Context,
private val playbackTracker: VoiceMessagePlaybackTracker,
voiceRecorderProvider: VoiceRecorderProvider
private val voiceRecorder: VoiceRecorder
) {
private var mediaPlayer: MediaPlayer? = null
private var voiceRecorder: VoiceRecorder = voiceRecorderProvider.provideVoiceRecorder()
private val amplitudeList = mutableListOf<Int>()

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.voice
import android.content.Context
import android.os.Build
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import timber.log.Timber
import java.io.File
import javax.inject.Inject
class VoicePlayerHelper @Inject constructor(
context: Context
) {
private val outputDirectory: File by lazy {
File(context.cacheDir, "voice_records").also {
it.mkdirs()
}
}
/**
* Ensure the file is encoded using aac audio codec
*/
fun convertFile(file: File): File? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Nothing to do
file
} else {
// Convert to mp4
val targetFile = File(outputDirectory, "Voice.mp4")
if (targetFile.exists()) {
targetFile.delete()
}
val start = System.currentTimeMillis()
val session = FFmpegKit.execute("-i \"${file.path}\" -c:a aac \"${targetFile.path}\"")
val duration = System.currentTimeMillis() - start
Timber.d("Convert to mp4 in $duration ms. Size in bytes from ${file.length()} to ${targetFile.length()}")
return when {
ReturnCode.isSuccess(session.returnCode) -> {
// SUCCESS
targetFile
}
ReturnCode.isCancel(session.returnCode) -> {
// CANCEL
null
}
else -> {
// FAILURE
Timber.e("Command failed with state ${session.state} and rc ${session.returnCode}.${session.failStackTrace}")
// TODO throw?
null
}
}
}
}
}