依存関係の更新。QRコード生成ライブラリの変更。
This commit is contained in:
parent
593fffaad2
commit
5f7f4c34ec
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.20" />
|
||||
<option name="version" value="1.9.22" />
|
||||
</component>
|
||||
</project>
|
|
@ -65,5 +65,7 @@ dependencies {
|
|||
api(project(":apng"))
|
||||
implementation(project(":base"))
|
||||
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:${Vers.desugarLibVersion}")
|
||||
|
||||
testImplementation("junit:junit:${Vers.junitVersion}")
|
||||
}
|
||||
|
|
|
@ -150,7 +150,6 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
// desugar_jdk_libs 2.0.0 は AGP 7.4.0-alpha10 以降を要求する
|
||||
|
@ -172,7 +171,8 @@ dependencies {
|
|||
implementation("com.github.UnifiedPush:android-connector:2.1.1")
|
||||
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
||||
|
||||
implementation("com.github.androidmads:QRGenerator:1.0.1")
|
||||
// implementation("com.github.androidmads:QRGenerator:1.0.1")
|
||||
implementation("com.github.alexzhirkevich:custom-qr-generator:1.6.2")
|
||||
|
||||
val apng4AndroidVersion = "2.25.0"
|
||||
implementation("com.github.penfeizhou.android.animation:apng:$apng4AndroidVersion")
|
||||
|
|
|
@ -1,95 +1,101 @@
|
|||
package jp.juggler.subwaytooter.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidmads.library.qrgenearator.QRGContents
|
||||
import androidmads.library.qrgenearator.QRGEncoder
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.alexzhirkevich.customqrgenerator.QrData
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawable
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.createQrVectorOptions
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorBallShape
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorColor
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorFrameShape
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogoPadding
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogoShape
|
||||
import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorPixelShape
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.util.coroutine.launchProgress
|
||||
import jp.juggler.subwaytooter.databinding.DlgQrCodeBinding
|
||||
import jp.juggler.util.coroutine.AppDispatchers
|
||||
import jp.juggler.util.coroutine.launchAndShowError
|
||||
import jp.juggler.util.coroutine.withProgress
|
||||
import jp.juggler.util.log.LogCategory
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object DlgQRCode {
|
||||
import jp.juggler.util.os.resDrawable
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private val log = LogCategory("DlgQRCode")
|
||||
|
||||
internal interface QrCodeCallback {
|
||||
fun onQrCode(bitmap: Bitmap?)
|
||||
val UInt.int get() = toInt()
|
||||
|
||||
fun AppCompatActivity.dialogQrCode(
|
||||
message: CharSequence,
|
||||
url: String,
|
||||
) = launchAndShowError("dialogQrCode failed.") {
|
||||
val drawable = withProgress(
|
||||
caption = getString(R.string.generating_qr_code),
|
||||
) {
|
||||
withContext(AppDispatchers.DEFAULT) {
|
||||
QrCodeDrawable(data = QrData.Url(url), options = qrCodeOptions())
|
||||
}
|
||||
}
|
||||
val dialog = Dialog(this@dialogQrCode)
|
||||
|
||||
val views = DlgQrCodeBinding.inflate(layoutInflater).apply {
|
||||
btnCancel.setOnClickListener { dialog.cancel() }
|
||||
ivQrCode.setImageDrawable(drawable)
|
||||
tvMessage.text = message
|
||||
tvUrl.text = "[ $url ]" // なぜか素のURLだと@以降が表示されない
|
||||
}
|
||||
|
||||
private fun makeQrCode(
|
||||
activity: ActMain,
|
||||
size: Int,
|
||||
url: String,
|
||||
callback: QrCodeCallback,
|
||||
) {
|
||||
activity.launchProgress(
|
||||
"making QR code",
|
||||
progressInitializer = {
|
||||
it.setMessageEx(activity.getString(R.string.generating_qr_code))
|
||||
},
|
||||
doInBackground = {
|
||||
try {
|
||||
QRGEncoder(
|
||||
/* data */ url,
|
||||
/* bundle */ null,
|
||||
QRGContents.Type.TEXT,
|
||||
/* dimension */ size,
|
||||
).apply {
|
||||
// 背景色
|
||||
colorBlack = Color.WHITE
|
||||
// 図柄の色
|
||||
colorWhite = Color.BLACK
|
||||
}.bitmap
|
||||
} catch (ex: Throwable) {
|
||||
log.e(ex, "QR generation failed.")
|
||||
null
|
||||
dialog.apply {
|
||||
setContentView(views.root)
|
||||
setCancelable(true)
|
||||
setCanceledOnTouchOutside(true)
|
||||
show()
|
||||
}
|
||||
},
|
||||
afterProc = {
|
||||
if (it != null) callback.onQrCode(it)
|
||||
},
|
||||
}
|
||||
|
||||
private fun AppCompatActivity.qrCodeOptions() = createQrVectorOptions {
|
||||
background {
|
||||
drawable = ColorDrawable(Color.WHITE)
|
||||
}
|
||||
|
||||
padding = .125f
|
||||
|
||||
logo {
|
||||
drawable = resDrawable(R.drawable.qr_code_center)
|
||||
size = .25f
|
||||
shape = QrVectorLogoShape.Default
|
||||
padding = QrVectorLogoPadding.Natural(.1f)
|
||||
}
|
||||
shapes {
|
||||
// 市松模様のドット
|
||||
darkPixel = QrVectorPixelShape.RoundCorners(.5f)
|
||||
// 3隅の真ん中の大きめドット
|
||||
ball = QrVectorBallShape.RoundCorners(.25f)
|
||||
// 3隅の枠
|
||||
frame = QrVectorFrameShape.RoundCorners(.25f)
|
||||
}
|
||||
colors {
|
||||
val cobalt = 0xFF0088FFU.int
|
||||
val cobaltDark = 0xFF004488U.int
|
||||
// 市松模様のドット
|
||||
dark = QrVectorColor.Solid(cobaltDark)
|
||||
// 3隅の真ん中の大きめドット
|
||||
ball = QrVectorColor.RadialGradient(
|
||||
colors = listOf(
|
||||
0f to cobaltDark,
|
||||
1f to cobalt,
|
||||
),
|
||||
radius = 2f,
|
||||
)
|
||||
// 3隅の枠
|
||||
frame = QrVectorColor.LinearGradient(
|
||||
colors = listOf(
|
||||
0f to cobaltDark,
|
||||
1f to cobalt,
|
||||
),
|
||||
orientation = QrVectorColor.LinearGradient
|
||||
.Orientation.Vertical
|
||||
)
|
||||
}
|
||||
|
||||
fun open(activity: ActMain, message: CharSequence, url: String) {
|
||||
|
||||
val size = (0.5f + 240f * activity.density).toInt()
|
||||
makeQrCode(activity, size, url, object : QrCodeCallback {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onQrCode(bitmap: Bitmap?) {
|
||||
|
||||
val viewRoot = activity.layoutInflater.inflate(R.layout.dlg_qr_code, null, false)
|
||||
val dialog = Dialog(activity)
|
||||
dialog.setContentView(viewRoot)
|
||||
dialog.setCancelable(true)
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
|
||||
var tv = viewRoot.findViewById<TextView>(R.id.tvMessage)
|
||||
tv.text = message
|
||||
|
||||
tv = viewRoot.findViewById(R.id.tvUrl)
|
||||
tv.text = "[ $url ]" // なぜか素のURLだと@以降が表示されない
|
||||
|
||||
val iv = viewRoot.findViewById<ImageView>(R.id.ivQrCode)
|
||||
iv.setImageBitmap(bitmap)
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
iv.setImageDrawable(null)
|
||||
bitmap?.recycle()
|
||||
}
|
||||
|
||||
viewRoot.findViewById<View>(R.id.btnCancel).setOnClickListener { dialog.cancel() }
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import jp.juggler.subwaytooter.column.Column
|
|||
import jp.juggler.subwaytooter.column.ColumnType
|
||||
import jp.juggler.subwaytooter.databinding.DlgContextMenuBinding
|
||||
import jp.juggler.subwaytooter.dialog.DlgListMember
|
||||
import jp.juggler.subwaytooter.dialog.DlgQRCode
|
||||
import jp.juggler.subwaytooter.dialog.dialogQrCode
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.PrefI
|
||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||
|
@ -446,11 +446,11 @@ internal class DlgContextMenu(
|
|||
else -> whoHost
|
||||
}
|
||||
|
||||
private fun updateGroup(btn: Button, group: View, toggle: Boolean = false): Boolean {
|
||||
private fun updateGroup(btn: Button, group: View, toggle: Boolean = false) {
|
||||
|
||||
if (btn.visibility != View.VISIBLE) {
|
||||
group.vg(false)
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
|
@ -463,19 +463,18 @@ internal class DlgContextMenu(
|
|||
else -> btn.setOnClickListener(this)
|
||||
}
|
||||
|
||||
val iconId = if (group.visibility == View.VISIBLE) {
|
||||
R.drawable.ic_arrow_drop_up
|
||||
} else {
|
||||
R.drawable.ic_arrow_drop_down
|
||||
val iconId = when (group.visibility) {
|
||||
View.VISIBLE -> R.drawable.ic_arrow_drop_up
|
||||
else -> R.drawable.ic_arrow_drop_down
|
||||
}
|
||||
|
||||
val iconColor = activity.attrColor(R.attr.colorTimeSmall)
|
||||
val drawable = createColoredDrawable(activity, iconId, iconColor, 1f)
|
||||
btn.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onClickUpdateGroup(v: View): Boolean = when (v.id) {
|
||||
private fun onClickUpdateGroup(v: View): Boolean {
|
||||
when (v.id) {
|
||||
R.id.btnGroupStatusCrossAccount -> updateGroup(
|
||||
views.btnGroupStatusCrossAccount,
|
||||
views.llGroupStatusCrossAccount,
|
||||
|
@ -487,27 +486,34 @@ internal class DlgContextMenu(
|
|||
views.llGroupUserCrossAccount,
|
||||
toggle = true
|
||||
)
|
||||
|
||||
R.id.btnGroupStatusAround -> updateGroup(
|
||||
views.btnGroupStatusAround,
|
||||
views.llGroupStatusAround,
|
||||
toggle = true
|
||||
)
|
||||
|
||||
R.id.btnGroupStatusByMe -> updateGroup(
|
||||
views.btnGroupStatusByMe,
|
||||
views.llGroupStatusByMe,
|
||||
toggle = true
|
||||
)
|
||||
|
||||
R.id.btnGroupStatusExtra -> updateGroup(
|
||||
views.btnGroupStatusExtra,
|
||||
views.llGroupStatusExtra,
|
||||
toggle = true
|
||||
)
|
||||
|
||||
R.id.btnGroupUserExtra -> updateGroup(
|
||||
views.btnGroupUserExtra,
|
||||
views.llGroupUserExtra,
|
||||
toggle = true
|
||||
)
|
||||
else -> false
|
||||
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun ActMain.onClickUserAndStatus(
|
||||
|
@ -552,16 +558,18 @@ internal class DlgContextMenu(
|
|||
accessInfo,
|
||||
who
|
||||
)
|
||||
|
||||
R.id.btnNickname -> clickNicknameCustomize(accessInfo, who)
|
||||
R.id.btnAccountQrCode -> DlgQRCode.open(
|
||||
activity,
|
||||
whoRef.decoded_display_name,
|
||||
who.getUserUrl()
|
||||
R.id.btnAccountQrCode -> activity.dialogQrCode(
|
||||
message = whoRef.decoded_display_name,
|
||||
url = who.getUserUrl()
|
||||
)
|
||||
|
||||
R.id.btnDomainBlock -> clickDomainBlock(accessInfo, who)
|
||||
R.id.btnOpenTimeline -> who.apiHost.valid()?.let { timelineLocal(pos, it) }
|
||||
R.id.btnDomainTimeline -> who.apiHost.valid()
|
||||
?.let { timelineDomain(pos, accessInfo, it) }
|
||||
|
||||
R.id.btnAvatarImage -> openAvatarImage(who)
|
||||
R.id.btnQuoteName -> quoteName(who)
|
||||
R.id.btnHideBoost -> userSetShowBoosts(accessInfo, who, false)
|
||||
|
@ -574,6 +582,7 @@ internal class DlgContextMenu(
|
|||
column,
|
||||
getUserApiHost()
|
||||
)
|
||||
|
||||
R.id.btnEndorse -> userEndorsement(accessInfo, who, !relation.endorsed)
|
||||
R.id.btnCopyAccountId -> who.id.toString().copyToClipboard(activity)
|
||||
R.id.btnOpenAccountInAdminWebUi -> openBrowser("https://${accessInfo.apiHost.ascii}/admin/accounts/${who.id}")
|
||||
|
@ -612,6 +621,7 @@ internal class DlgContextMenu(
|
|||
accessInfo,
|
||||
status
|
||||
)
|
||||
|
||||
R.id.btnQuoteUrlStatus -> openPost(status.url?.notEmpty())
|
||||
R.id.btnShareUrlStatus -> shareText(status.url?.notEmpty())
|
||||
R.id.btnConversationMute -> conversationMute(accessInfo, status)
|
||||
|
@ -622,6 +632,7 @@ internal class DlgContextMenu(
|
|||
accessInfo,
|
||||
status
|
||||
)
|
||||
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
|
@ -675,10 +686,12 @@ internal class DlgContextMenu(
|
|||
dialog.dismissSafe()
|
||||
followFromAnotherAccount(pos, accessInfo, who)
|
||||
}
|
||||
|
||||
R.id.btnProfile -> {
|
||||
dialog.dismissSafe()
|
||||
userProfileFromAnotherAccount(pos, accessInfo, who)
|
||||
}
|
||||
|
||||
R.id.btnSendMessage -> {
|
||||
dialog.dismissSafe()
|
||||
mentionFromAnotherAccount(accessInfo, who)
|
||||
|
|
|
@ -20,7 +20,7 @@ import jp.juggler.util.log.LogCategory
|
|||
import jp.juggler.util.log.errorEx
|
||||
import jp.juggler.util.log.showToast
|
||||
import jp.juggler.util.log.withCaption
|
||||
import jp.juggler.util.media.MovideResizeMode
|
||||
import jp.juggler.util.media.MovieResizeMode
|
||||
import jp.juggler.util.media.MovieResizeConfig
|
||||
import jp.juggler.util.media.ResizeConfig
|
||||
import jp.juggler.util.media.ResizeType
|
||||
|
@ -928,7 +928,7 @@ class SavedAccount(
|
|||
|
||||
fun getMovieResizeConfig() =
|
||||
MovieResizeConfig(
|
||||
mode = MovideResizeMode.fromInt(movieTranscodeMode),
|
||||
mode = MovieResizeMode.fromInt(movieTranscodeMode),
|
||||
limitBitrate = movieTranscodeBitrate.toLongOrNull()
|
||||
?.takeIf { it >= 100_000L } ?: 2_000_000L,
|
||||
limitFrameRate = movieTranscodeFramerate.toIntOrNull()
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="64dp"/>
|
||||
<stroke android:color="#08f" android:width="10dp"/>
|
||||
</shape>
|
|
@ -30,8 +30,8 @@
|
|||
tools:ignore="SmallSp"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="240dp"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="280dp"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:id="@+id/ivQrCode"
|
||||
|
|
|
@ -77,7 +77,7 @@ dependencies {
|
|||
api("androidx.emoji2:emoji2-views-helper:${Vers.emoji2Version}")
|
||||
api("androidx.emoji2:emoji2-views:${Vers.emoji2Version}")
|
||||
api("androidx.emoji2:emoji2:${Vers.emoji2Version}")
|
||||
api("androidx.exifinterface:exifinterface:1.3.6")
|
||||
api("androidx.exifinterface:exifinterface:1.3.7")
|
||||
api("androidx.lifecycle:lifecycle-common-java8:${Vers.lifecycleVersion}")
|
||||
api("androidx.lifecycle:lifecycle-livedata-ktx:${Vers.lifecycleVersion}")
|
||||
api("androidx.lifecycle:lifecycle-process:${Vers.lifecycleVersion}")
|
||||
|
@ -100,8 +100,6 @@ dependencies {
|
|||
api("com.otaliastudios:transcoder:0.10.5")
|
||||
api("com.squareup.okhttp3:okhttp-urlconnection:${Vers.okhttpVersion}")
|
||||
api("com.squareup.okhttp3:okhttp:${Vers.okhttpVersion}")
|
||||
// api( "io.github.inflationx:calligraphy3:3.1.1")
|
||||
// api( "io.github.inflationx:viewpump:2.1.1")
|
||||
api("org.bouncycastle:bcprov-jdk15on:1.70")
|
||||
api("org.jetbrains.kotlin:kotlin-reflect:${Vers.kotlinVersion}")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-android:${Vers.kotlinxCoroutinesVersion}")
|
||||
|
|
|
@ -96,11 +96,7 @@ suspend fun transcodeAudio(
|
|||
}
|
||||
val transformer = Transformer.Builder(context)
|
||||
.setLooper(looper)
|
||||
.setTransformationRequest(
|
||||
TransformationRequest.Builder()
|
||||
.setAudioMimeType(encodeMimeType)
|
||||
.build()
|
||||
)
|
||||
.addListener(transformerListener)
|
||||
.build()
|
||||
|
||||
|
|
|
@ -153,7 +153,8 @@ fun AppCompatActivity.overrideActivityTransitionCompat(
|
|||
@AnimRes animEnter: Int,
|
||||
@AnimRes animExit: Int,
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= 34 -> {
|
||||
overrideActivityTransition(
|
||||
when (overrideType) {
|
||||
TransitionOverrideType.Open ->
|
||||
|
@ -165,13 +166,17 @@ fun AppCompatActivity.overrideActivityTransitionCompat(
|
|||
animEnter,
|
||||
animExit
|
||||
)
|
||||
} else {
|
||||
}
|
||||
|
||||
else -> {
|
||||
@Suppress("DEPRECATION")
|
||||
overridePendingTransition(
|
||||
animEnter,
|
||||
animExit,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//object Utils {
|
||||
|
|
|
@ -84,6 +84,30 @@ fun AppCompatActivity.launchAndShowError(
|
|||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
suspend fun <T:Any?> AppCompatActivity.withProgress(
|
||||
caption:String,
|
||||
progressInitializer: suspend (ProgressDialogEx) -> Unit = {},
|
||||
block: suspend (progress :ProgressDialogEx)->T,
|
||||
):T {
|
||||
val activity = this
|
||||
var progress: ProgressDialogEx? = null
|
||||
try {
|
||||
progress = ProgressDialogEx(activity)
|
||||
progress.setCancelable(true)
|
||||
progress.isIndeterminateEx = true
|
||||
progress.setMessageEx(caption)
|
||||
progressInitializer(progress)
|
||||
progress.show()
|
||||
return supervisorScope {
|
||||
val task = async(AppDispatchers.MainImmediate) { block(progress) }
|
||||
progress.setOnCancelListener { task.cancel() }
|
||||
task.await()
|
||||
}
|
||||
} finally {
|
||||
progress?.dismissSafe()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Any?> AppCompatActivity.launchProgress(
|
||||
caption: String,
|
||||
doInBackground: suspend CoroutineScope.(ProgressDialogEx) -> T,
|
||||
|
|
|
@ -289,18 +289,14 @@ fun createResizedBitmap(
|
|||
// 出力用Bitmap作成
|
||||
val dst = Bitmap.createBitmap(dstSizeInt.x, dstSizeInt.y, Bitmap.Config.ARGB_8888)
|
||||
try {
|
||||
if (dst == null) {
|
||||
context.showToast(false, "bitmap creation failed.")
|
||||
} else {
|
||||
val canvas = Canvas(dst)
|
||||
val paint = Paint()
|
||||
paint.isFilterBitmap = true
|
||||
canvas.drawBitmap(sourceBitmap, matrix, paint)
|
||||
log.d("createResizedBitmap: resized to ${dstSizeInt.x}x${dstSizeInt.y}")
|
||||
return dst
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
dst?.recycle()
|
||||
dst.recycle()
|
||||
throw ex
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -12,7 +12,6 @@ import androidx.media3.transformer.Effects
|
|||
import androidx.media3.transformer.ExportException
|
||||
import androidx.media3.transformer.ExportResult
|
||||
import androidx.media3.transformer.ProgressHolder
|
||||
import androidx.media3.transformer.TransformationRequest
|
||||
import androidx.media3.transformer.Transformer
|
||||
import androidx.media3.transformer.VideoEncoderSettings
|
||||
import com.otaliastudios.transcoder.Transcoder
|
||||
|
@ -23,9 +22,14 @@ import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy
|
|||
import jp.juggler.util.coroutine.AppDispatchers
|
||||
import jp.juggler.util.data.clip
|
||||
import jp.juggler.util.log.LogCategory
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
@ -38,19 +42,19 @@ import kotlin.math.sqrt
|
|||
|
||||
private val log = LogCategory("MovieUtils")
|
||||
|
||||
enum class MovideResizeMode(val int: Int) {
|
||||
enum class MovieResizeMode(val int: Int) {
|
||||
Auto(0),
|
||||
No(1),
|
||||
Always(2),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromInt(i: Int) = values().find { it.int == i } ?: Auto
|
||||
fun fromInt(i: Int) = entries.find { it.int == i } ?: Auto
|
||||
}
|
||||
}
|
||||
|
||||
data class MovieResizeConfig(
|
||||
val mode: MovideResizeMode,
|
||||
val mode: MovieResizeMode,
|
||||
val limitFrameRate: Int,
|
||||
val limitBitrate: Long,
|
||||
val limitSquarePixels: Int,
|
||||
|
@ -78,9 +82,9 @@ data class MovieResizeConfig(
|
|||
|
||||
// トランスコードをスキップする判定
|
||||
fun isTranscodeRequired(info: VideoInfo) = when (mode) {
|
||||
MovideResizeMode.No -> false
|
||||
MovideResizeMode.Always -> true
|
||||
MovideResizeMode.Auto ->
|
||||
MovieResizeMode.No -> false
|
||||
MovieResizeMode.Always -> true
|
||||
MovieResizeMode.Auto ->
|
||||
info.squarePixels > limitSquarePixels ||
|
||||
(info.actualBps ?: 0).toFloat() > limitBitrate.toFloat() * 1.5f ||
|
||||
(info.frameRatio == null || info.frameRatio < 1f || info.frameRatio > limitFrameRate)
|
||||
|
@ -129,9 +133,9 @@ suspend fun transcodeVideoMedia3Transformer(
|
|||
withContext(AppDispatchers.MainImmediate) {
|
||||
|
||||
when (resizeConfig.mode) {
|
||||
MovideResizeMode.No -> return@withContext inFile
|
||||
MovideResizeMode.Always -> Unit
|
||||
MovideResizeMode.Auto -> {
|
||||
MovieResizeMode.No -> return@withContext inFile
|
||||
MovieResizeMode.Always -> Unit
|
||||
MovieResizeMode.Auto -> {
|
||||
if (!resizeConfig.isTranscodeRequired(info)) {
|
||||
log.i("transcodeVideoMedia3Transformer: transcode not required.")
|
||||
return@withContext inFile
|
||||
|
@ -189,12 +193,6 @@ suspend fun transcodeVideoMedia3Transformer(
|
|||
}
|
||||
}.build()
|
||||
|
||||
val request = TransformationRequest.Builder().apply {
|
||||
setVideoMimeType(MimeTypes.VIDEO_H264)
|
||||
setAudioMimeType(MimeTypes.AUDIO_AAC)
|
||||
// ビットレートがないな…
|
||||
}.build()
|
||||
|
||||
// 完了検知
|
||||
val completed = AtomicBoolean(false)
|
||||
val error = AtomicReference<Throwable>(null)
|
||||
|
@ -226,9 +224,11 @@ suspend fun transcodeVideoMedia3Transformer(
|
|||
// 開始
|
||||
val transformer = Transformer.Builder(context).apply {
|
||||
setEncoderFactory(encoderFactory)
|
||||
setTransformationRequest(request)
|
||||
setAudioMimeType(MimeTypes.AUDIO_AAC)
|
||||
setVideoMimeType(MimeTypes.VIDEO_H264)
|
||||
addListener(listener)
|
||||
}.build()
|
||||
|
||||
transformer.start(editedMediaItem, outFile.canonicalPath)
|
||||
|
||||
// 完了まで待機しつつ、定期的に進捗コールバックを呼ぶ
|
||||
|
@ -265,9 +265,9 @@ suspend fun transcodeVideo(
|
|||
}
|
||||
|
||||
when (resizeConfig.mode) {
|
||||
MovideResizeMode.No -> return@withContext inFile
|
||||
MovideResizeMode.Always -> Unit
|
||||
MovideResizeMode.Auto -> {
|
||||
MovieResizeMode.No -> return@withContext inFile
|
||||
MovieResizeMode.Always -> Unit
|
||||
MovieResizeMode.Auto -> {
|
||||
if (info.squarePixels <= resizeConfig.limitSquarePixels &&
|
||||
(info.actualBps ?: 0).toFloat() <= resizeConfig.limitBitrate * 1.5f &&
|
||||
(info.frameRatio?.toInt() ?: 0) <= resizeConfig.limitFrameRate
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package jp.juggler.util.os
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
/**
|
||||
* インストゥルメントテストのContextは
|
||||
|
@ -18,3 +20,6 @@ val Context.applicationContextSafe: Context
|
|||
|
||||
fun Context.error(@StringRes resId: Int, vararg args: Any?): Nothing =
|
||||
error(getString(resId, *args))
|
||||
|
||||
fun Context.resDrawable(@DrawableRes resId: Int) =
|
||||
ContextCompat.getDrawable(this, resId)
|
||||
|
|
|
@ -31,7 +31,7 @@ allprojects {
|
|||
google()
|
||||
mavenCentral()
|
||||
|
||||
// com.github.androidmads:QRGenerator
|
||||
// alexzhirkevich/custom-qr-generator
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,49 @@
|
|||
import org.gradle.api.JavaVersion
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object Vers {
|
||||
const val stBuildToolsVersion = "34.0.0"
|
||||
const val stCompileSdkVersion = 34
|
||||
const val stTargetSdkVersion = 34
|
||||
const val stMinSdkVersion = 26
|
||||
|
||||
val javaSourceCompatibility = JavaVersion.VERSION_1_8
|
||||
val javaTargetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
const val androidGradlePruginVersion = "8.1.4"
|
||||
const val kotlinVersion = "1.9.22"
|
||||
const val kotlinJvmTarget = "1.8"
|
||||
const val kotlinJvmToolchain = 17
|
||||
|
||||
const val androidGradlePruginVersion = "8.2.1"
|
||||
|
||||
const val androidxAnnotationVersion = "1.6.0"
|
||||
const val androidxTestEspressoCoreVersion = "3.5.1"
|
||||
const val androidxTestExtJunitVersion = "1.1.5"
|
||||
const val androidxTestVersion = "1.5.0"
|
||||
const val ankoVersion = "0.10.8"
|
||||
|
||||
// const val ankoVersion = "0.10.8"
|
||||
const val appcompatVersion = "1.6.1"
|
||||
const val archVersion = "2.2.0"
|
||||
const val commonsCodecVersion = "1.16.0"
|
||||
const val composeVersion = "1.0.5"
|
||||
const val conscryptVersion = "2.5.2"
|
||||
const val coreKtxVersion = "1.12.0"
|
||||
const val desugarLibVersion = "2.0.3"
|
||||
const val desugarLibVersion = "2.0.4"
|
||||
const val detektVersion = "1.23.4"
|
||||
const val emoji2Version = "1.4.0"
|
||||
const val glideVersion = "4.15.1"
|
||||
const val junitVersion = "4.13.2"
|
||||
const val koinVersion = "3.5.0"
|
||||
const val kotlinJvmTarget = "1.8"
|
||||
const val kotlinJvmToolchain = 17
|
||||
const val kotlinTestVersion = "1.9.20"
|
||||
const val kotlinVersion = "1.9.20"
|
||||
const val kotlinTestVersion = kotlinVersion // "1.9.22"
|
||||
const val kotlinxCoroutinesVersion = "1.7.3"
|
||||
const val kspVersion = "1.9.20-1.0.14"
|
||||
const val kspVersion = "$kotlinVersion-1.0.16"
|
||||
const val lifecycleVersion = "2.6.2"
|
||||
const val materialVersion = "1.10.0"
|
||||
const val materialVersion = "1.11.0"
|
||||
const val media3Version = "1.2.0"
|
||||
const val okhttpVersion = "5.0.0-alpha.11"
|
||||
const val preferenceKtxVersion = "1.2.1"
|
||||
const val stBuildToolsVersion = "34.0.0"
|
||||
const val stCompileSdkVersion = 34
|
||||
const val stMinSdkVersion = 26
|
||||
const val stTargetSdkVersion = 34
|
||||
const val startupVersion = "1.1.1"
|
||||
const val testKtxVersion = "1.5.0"
|
||||
const val webpDecoderVersion = "2.6.$glideVersion"
|
||||
const val workVersion = "2.8.1"
|
||||
const val workVersion = "2.9.0"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Mon Jun 13 20:53:58 JST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -40,6 +40,7 @@ android {
|
|||
compileOptions {
|
||||
sourceCompatibility = Vers.javaSourceCompatibility
|
||||
targetCompatibility = Vers.javaTargetCompatibility
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
|
Loading…
Reference in New Issue