add aac,3gp,m4a audio format in media attachment

This commit is contained in:
tateisu 2019-07-19 17:58:05 +09:00
parent 5c72dc52e5
commit d97dc031df
7 changed files with 181 additions and 72 deletions

View File

@ -34,6 +34,7 @@
<w>flac</w>
<w>flexbox</w>
<w>foregrounder</w>
<w>ftyp</w>
<w>gifv</w>
<w>github</w>
<w>gmail</w>
@ -45,6 +46,7 @@
<w>idat</w>
<w>idempotency</w>
<w>ihdr</w>
<w>kddi</w>
<w>kenglxn</w>
<w>kotlinx</w>
<w>mailto</w>

View File

@ -135,6 +135,18 @@
<data android:mimeType="video/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*"/>
</intent-filter>
</activity>
<activity

View File

@ -54,7 +54,7 @@ class ActCallback : AppCompatActivity() {
// ACTION_SEND か ACTION_SEND_MULTIPLE
Intent.ACTION_SEND == action
|| Intent.ACTION_SEND_MULTIPLE == action
// ACTION_VIEW かつ type が 画像かビデオ
// ACTION_VIEW かつ type が 画像かビデオか音声
|| Intent.ACTION_VIEW == action && type.isMediaMimeType() ) {
// Google Photo などから送られるIntentに含まれるuriの有効期間はActivityが閉じられるまで

View File

@ -92,6 +92,7 @@ class ActPost : AppCompatActivity(),
private const val REQUEST_CODE_MUSHROOM = 3
private const val REQUEST_CODE_VIDEO = 4
private const val REQUEST_CODE_ATTACHMENT_OLD = 5
private const val REQUEST_CODE_SOUND = 6
private const val PERMISSION_REQUEST_CODE = 1
@ -123,6 +124,11 @@ class ActPost : AppCompatActivity(),
add("audio/x-pn-wav")
add("audio/flac")
add("audio/x-flac")
// https://github.com/tootsuite/mastodon/pull/11342
add("audio/aac")
add("audio/m4a")
add("audio/3gpp")
}
private val imageHeaderList = arrayOf(
@ -152,6 +158,44 @@ class ActPost : AppCompatActivity(),
)
)
private val sig3gp = arrayOf(
"3ge6",
"3ge7",
"3gg6",
"3gp1",
"3gp2",
"3gp3",
"3gp4",
"3gp5",
"3gp6",
"3gp7",
"3gr6",
"3gr7",
"3gs6",
"3gs7",
"kddi"
).map { it.toCharArray().toLowerByteArray() }
private val sigM4a = arrayOf(
"M4A ",
"M4B ",
"M4P "
).map { it.toCharArray().toLowerByteArray() }
private val sigFtyp = "ftyp".toCharArray().toLowerByteArray()
private fun matchSig(
data : ByteArray,
dataOffset : Int,
sig : ByteArray,
sigSize : Int = sig.size
) : Boolean {
for(i in 0 until sigSize) {
if(data[dataOffset + i] != sig[i]) return false
}
return true
}
private fun findMimeTypeByFileHeader(
contentResolver : ContentResolver,
uri : Uri
@ -165,27 +209,60 @@ class ActPost : AppCompatActivity(),
val header = pair.second
if(nRead >= header.size && data.startWith(header)) return type
}
// scan mp3 frame header
loop@ for(i in 0 until nRead - 4) {
// scan frame header
for(i in 0 until nRead - 8) {
if(! matchSig(data, i, sigFtyp)) continue
// 3gpp check
for(s in sig3gp) {
if(matchSig(data, i + 4, s)) return "audio/3gpp"
}
// m4a check
for(s in sigM4a) {
if(matchSig(data, i + 4, s)) return "audio/m4a"
}
}
// scan frame header
loop@ for(i in 0 until nRead - 2) {
// mpeg frame header
val b0 = data[i].toInt() and 255
if(b0 != 255) continue
val b1 = data[i + 1].toInt() and 255
if((b1 and 0b11100000) != 0b11100000) continue
// mpegVersionId
when(((b1 shr 3) and 3)) {
1 -> continue@loop
}
val mpegVersionId = ((b1 shr 3) and 3)
// 00 mpeg 2.5
// 01 not used
// 10 (mp3) mpeg 2 / (AAC) mpeg-4
// 11 (mp3) mpeg 1 / (AAC) mpeg-2
// mpegLayerId
when(((b1 shr 1) and 3)) {
1 -> {
}
@Suppress("MoveVariableDeclarationIntoWhen")
val mpegLayerId = ((b1 shr 1) and 3)
// 00 (mp3)not used / (AAC) always 0
// 01 (mp3)layer III
// 10 (mp3)layer II
// 11 (mp3)layer I
else -> continue@loop
}
when(mpegLayerId) {
0 -> when(mpegVersionId) {
2, 3 -> return "audio/aac"
return "audio/mp3"
else -> {
}
}
1 -> when(mpegVersionId) {
0, 2, 3 -> return "audio/mp3"
else -> {
}
}
}
}
}
} catch(ex : Throwable) {
@ -193,16 +270,6 @@ class ActPost : AppCompatActivity(),
}
return null
}
// private void performCameraVideo(){
//
// try{
// Intent takeVideoIntent = new Intent( MediaStore.ACTION_VIDEO_CAPTURE );
// startActivityForResult( takeVideoIntent, REQUEST_CODE_VIDEO );
// }catch( Throwable ex ){
// warning.trace( ex );
// Utils.showToast( this, ex, "opening video app failed." );
// }
// }
/////////////////////////////////////////////////
@ -422,22 +489,31 @@ class ActPost : AppCompatActivity(),
}
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
if(requestCode == REQUEST_CODE_ATTACHMENT_OLD && resultCode == Activity.RESULT_OK) {
checkAttachments(data?.handleGetContentResult(contentResolver))
} else if(requestCode == REQUEST_CODE_ATTACHMENT && resultCode == Activity.RESULT_OK) {
checkAttachments(data?.handleGetContentResult(contentResolver))
} else if(requestCode == REQUEST_CODE_CAMERA) {
if(resultCode != Activity.RESULT_OK) {
if(resultCode != RESULT_OK) {
when(requestCode) {
REQUEST_CODE_CAMERA -> {
// 失敗したら DBからデータを削除
val uriCameraImage = this.uriCameraImage
if(uriCameraImage != null) {
contentResolver.delete(uriCameraImage, null, null)
this@ActPost.uriCameraImage = null
}
}
}
} else {
when(requestCode) {
REQUEST_CODE_ATTACHMENT_OLD -> checkAttachments(
data?.handleGetContentResult(
contentResolver
)
)
REQUEST_CODE_ATTACHMENT -> checkAttachments(
data?.handleGetContentResult(
contentResolver
)
)
REQUEST_CODE_CAMERA -> {
// 画像のURL
val uri = data?.data ?: uriCameraImage
if(uri != null) {
@ -446,12 +522,15 @@ class ActPost : AppCompatActivity(),
showToast(this@ActPost, false, "missing image uri")
}
}
} else if(requestCode == REQUEST_CODE_VIDEO && resultCode == Activity.RESULT_OK) {
data?.data?.let { addAttachment(it) }
} else if(requestCode == REQUEST_CODE_MUSHROOM && resultCode == Activity.RESULT_OK) {
REQUEST_CODE_VIDEO, REQUEST_CODE_SOUND -> data?.data?.let { addAttachment(it) }
REQUEST_CODE_MUSHROOM -> {
data?.getStringExtra("replace_key")?.let { applyMushroomResult(it) }
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
@ -1654,9 +1733,6 @@ class ActPost : AppCompatActivity(),
// }
val a = ActionsDialog()
a.addAction(getString(R.string.image_capture)) {
performCamera()
}
a.addAction(getString(R.string.pick_images)) {
openAttachmentChooser(R.string.pick_images, "image/*", "video/*")
}
@ -1666,12 +1742,24 @@ class ActPost : AppCompatActivity(),
a.addAction(getString(R.string.pick_audios)) {
openAttachmentChooser(R.string.pick_audios, "audio/*")
}
a.addAction(getString(R.string.image_capture)) {
performCamera()
}
a.addAction(getString(R.string.video_capture)) {
performCapture(
REQUEST_CODE_VIDEO,
MediaStore.ACTION_VIDEO_CAPTURE,
"can't open video capture app."
)
}
a.addAction(getString(R.string.voice_capture)) {
performCapture(
REQUEST_CODE_SOUND,
MediaStore.Audio.Media.RECORD_SOUND_ACTION,
"can't open voice capture app."
)
}
// a.addAction( getString( R.string.video_capture ), new Runnable() {
// @Override public void run(){
// performCameraVideo();
// }
// } );
a.show(this, null)
}
@ -2144,9 +2232,7 @@ class ActPost : AppCompatActivity(),
}
private fun performCamera() {
try {
// カメラで撮影
val filename = System.currentTimeMillis().toString() + ".jpg"
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, filename)
@ -2165,14 +2251,20 @@ class ActPost : AppCompatActivity(),
}
private fun performCapture(requestCode : Int, action : String, errorCaption : String) {
try {
startActivityForResult(Intent(action), requestCode)
} catch(ex : Throwable) {
log.trace(ex);
showToast(this, ex, errorCaption)
}
}
private fun preparePermission() {
if(Build.VERSION.SDK_INT >= 23) {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) // Manifest.permission.CAMERA,
,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
PERMISSION_REQUEST_CODE
)
} else {

View File

@ -31,3 +31,4 @@
<string name="more">עוד</string>
<string name="close_column">סגור את העמודה</string>
</resources>

View File

@ -349,6 +349,8 @@
<string name="image">画像</string>
<string name="image_alpha">画像の不透明度</string>
<string name="image_capture">カメラで撮影</string>
<string name="video_capture">動画を記録</string>
<string name="voice_capture">音声を記録</string>
<string name="in_reply_to_id_conversion_failed">アカウント切り替えできません。in_reply_toのID変換に失敗しました。</string>
<string name="input_access_token">アクセストークン</string>
<string name="input_access_token_desc">通常のユーザ認証の代わりにアクセストークンを指定します。(上級者向け)</string>
@ -737,7 +739,6 @@
<string name="version_is">バージョン %1$s</string>
<string name="vibration">振動</string>
<string name="video_buffering">バッファ中…</string>
<string name="video_capture">動画の撮影</string>
<string name="video_pick">動画の選択</string>
<string name="visibility">公開範囲</string>
<string name="visibility_direct">ダイレクト</string>

View File

@ -232,6 +232,8 @@
<string name="dont_resize">Don\'t resize</string>
<string name="long_side_pixel">Resize to %1$d pixels</string>
<string name="image_capture">Take a picture</string>
<string name="video_capture">Capture a video</string>
<string name="voice_capture">Capture a voice</string>
<string name="missing_permission_to_access_media">Missing app permission to access media.</string>
<string name="attachment_uploading">Uploading media attachment…</string>
<string name="attachment_uploaded">Media attachment was uploaded.</string>
@ -492,7 +494,6 @@
<string name="quote_all_hashtag_of">Quote all hashtags \"%1$s\"</string>
<string name="parsing_response">Parsing response…</string>
<string name="video_pick">Pick video</string>
<string name="video_capture">Capture video</string>
<string name="already_followed">Already followed.</string>
<string name="cant_get_web_setting_visibility">Can\'t get visibility setting in web app.</string>
<string name="mastodon_1_6_later">Mastodon 1.6 or later</string>