(Mastodon 開発版)音声の投稿に対応 f7f23b4a19

This commit is contained in:
tateisu 2019-06-20 09:22:42 +09:00
parent ac548cb582
commit 201a365401
10 changed files with 150 additions and 54 deletions

View File

@ -31,6 +31,7 @@
<w>favourited</w>
<w>fediverse</w>
<w>firebase</w>
<w>flac</w>
<w>flexbox</w>
<w>foregrounder</w>
<w>gifv</w>
@ -48,6 +49,7 @@
<w>kotlinx</w>
<w>mailto</w>
<w>mimumedon</w>
<w>mpeg</w>
<w>mpga</w>
<w>navi</w>
<w>nicodic</w>

View File

@ -1354,7 +1354,7 @@ class ActAccountSetting
private fun performAttachment(request_code : Int) {
try {
val intent = intentGetContent(false, getString(R.string.pick_image), "image/*")
val intent = intentGetContent(false, getString(R.string.pick_image), arrayOf("image/*"))
startActivityForResult(intent, request_code)
} catch(ex : Throwable) {
log.trace(ex, "performAttachment failed.")

View File

@ -22,6 +22,15 @@ class ActCallback : AppCompatActivity() {
internal val last_uri = AtomicReference<Uri>(null)
internal val sent_intent = AtomicReference<Intent>(null)
private fun String?.isMediaMimeType() =when{
this == null -> false
this.startsWith("image/") -> true
this.startsWith("video/") -> true
this.startsWith("audio/") -> true
else -> false
}
}
override fun onCreate(savedInstanceState : Bundle?) {
@ -46,9 +55,7 @@ class ActCallback : AppCompatActivity() {
Intent.ACTION_SEND == action
|| Intent.ACTION_SEND_MULTIPLE == action
// ACTION_VIEW かつ type が 画像かビデオ
|| Intent.ACTION_VIEW == action && type != null && (type.startsWith("image/") || type.startsWith(
"video/"
))) {
|| Intent.ACTION_VIEW == action && type.isMediaMimeType() ) {
// Google Photo などから送られるIntentに含まれるuriの有効期間はActivityが閉じられるまで
// http://qiita.com/pside/items/a821e2fe9ae6b7c1a98c
@ -92,7 +99,7 @@ class ActCallback : AppCompatActivity() {
val action = src.action
val type = src.type
if(type != null && (type.startsWith("image/") || type.startsWith("video/"))) {
if( type.isMediaMimeType() ) {
if(Intent.ACTION_VIEW == action) {
src.data?.let { uriOriginal ->
try {

View File

@ -188,7 +188,7 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
}
R.id.btnColumnBackgroundImage -> {
val intent = intentGetContent(false, getString(R.string.pick_image), "image/*")
val intent = intentGetContent(false, getString(R.string.pick_image), arrayOf("image/*"))
startActivityForResult(intent, REQUEST_CODE_PICK_BACKGROUND)
}

View File

@ -104,6 +104,7 @@ class ActPost : AppCompatActivity(),
//
add("image/*") // Android標準のギャラリーが image/* を出してくることがあるらしい
add("video/*") // Android標準のギャラリーが image/* を出してくることがあるらしい
add("audio/*") // Android標準のギャラリーが image/* を出してくることがあるらしい
//
add("image/jpeg")
add("image/png")
@ -111,6 +112,17 @@ class ActPost : AppCompatActivity(),
add("video/webm")
add("video/mp4")
add("video/quicktime")
//
add("audio/webm")
add("audio/ogg")
add("audio/mpeg")
add("audio/mp3")
add("audio/wav")
add("audio/wave")
add("audio/x-wav")
add("audio/x-pn-wav")
add("audio/flac")
add("audio/x-flac")
}
private val imageHeaderList = arrayOf(
@ -125,22 +137,59 @@ class ActPost : AppCompatActivity(),
Pair(
"image/gif",
charArrayOf('G', 'I', 'F').toLowerByteArray()
),
Pair(
"audio/wav",
charArrayOf('R', 'I', 'F', 'F').toLowerByteArray()
),
Pair(
"audio/ogg",
charArrayOf('O', 'g', 'g', 'S').toLowerByteArray()
),
Pair(
"audio/flac",
charArrayOf('f', 'L', 'a', 'C').toLowerByteArray()
)
)
private fun checkImageHeaderList(contentResolver : ContentResolver, uri : Uri) : String? {
private fun findMimeTypeByFileHeader(
contentResolver : ContentResolver,
uri : Uri
) : String? {
try {
contentResolver.openInputStream(uri)?.use { inStream ->
val data = ByteArray(32)
val data = ByteArray(65536)
val nRead = inStream.read(data, 0, data.size)
for(pair in imageHeaderList) {
val type = pair.first
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) {
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
}
// mpegLayerId
when(((b1 shr 1) and 3)) {
1 -> {
}
else -> continue@loop
}
return "audio/mp3"
}
}
} catch(ex : Throwable) {
log.e(ex, "checkImageHeaderList failed.")
log.e(ex, "findMimeTypeByFileHeader failed.")
}
return null
}
@ -710,7 +759,7 @@ class ActPost : AppCompatActivity(),
}
}
if( this.attachment_list.isNotEmpty() ) {
if(this.attachment_list.isNotEmpty()) {
cbNSFW.isChecked = base_status.sensitive == true
}
@ -729,7 +778,6 @@ class ActPost : AppCompatActivity(),
etContentWarning.setSelection(text.length)
cbContentWarning.isChecked = text.isNotEmpty()
val src_enquete = base_status.enquete
val src_items = src_enquete?.items
if(src_items != null) {
@ -817,7 +865,7 @@ class ActPost : AppCompatActivity(),
log.trace(ex)
}
}
if( this.attachment_list.isNotEmpty()) {
if(this.attachment_list.isNotEmpty()) {
cbNSFW.isChecked = item.sensitive
}
}
@ -1080,8 +1128,6 @@ class ActPost : AppCompatActivity(),
for(iv in ivMedia) {
iv.setOnClickListener(this)
iv.setDefaultImage(defaultColorIcon(this, R.drawable.ic_upload))
iv.setErrorImage(defaultColorIcon(this, R.drawable.ic_unknown))
}
cbContentWarning.setOnCheckedChangeListener { _, _ ->
@ -1406,10 +1452,27 @@ class ActPost : AppCompatActivity(),
iv.visibility = View.VISIBLE
val pa = attachment_list[idx]
val a = pa.attachment
if(pa.status == PostAttachment.STATUS_UPLOADED && a != null) {
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), a.preview_url)
} else {
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), null)
when {
a == null || pa.status != PostAttachment.STATUS_UPLOADED -> {
iv.setDefaultImage(defaultColorIcon(this, R.drawable.ic_upload))
iv.setErrorImage(defaultColorIcon(this, R.drawable.ic_unknown))
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), null)
}
else -> {
iv.setDefaultImage(defaultColorIcon(this, R.drawable.ic_upload))
iv.setErrorImage(
defaultColorIcon(
this,
when {
a.isAudio -> R.drawable.ic_music_note
else -> R.drawable.ic_unknown
}
)
)
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), a.preview_url)
}
}
}
}
@ -1422,24 +1485,25 @@ class ActPost : AppCompatActivity(),
showToast(this, false, ex.withCaption("can't get attachment item[$idx]."))
return
}
AlertDialog.Builder(this)
.setTitle(R.string.media_attachment)
.setItems(
arrayOf<CharSequence>(
getString(R.string.set_description),
getString(R.string.set_focus_point),
getString(R.string.delete)
)
) { _, i ->
when(i) {
0 -> editAttachmentDescription(pa)
1 -> openFocusPoint(pa)
2 -> deleteAttachment(pa)
}
val a = ActionsDialog()
.addAction( getString(R.string.set_description) ){
editAttachmentDescription(pa)
}
.setNegativeButton(R.string.cancel, null)
.show()
if( pa.attachment?.isAudio == true ){
// can't set focus
}else{
a.addAction(getString(R.string.set_focus_point)){
openFocusPoint(pa)
}
}
a.addAction(getString(R.string.delete)){
deleteAttachment(pa)
}
a.show(this,title = getString(R.string.media_attachment))
}
private fun openFocusPoint(pa : PostAttachment) {
@ -1591,8 +1655,18 @@ class ActPost : AppCompatActivity(),
// }
val a = ActionsDialog()
a.addAction(getString(R.string.pick_images)) { performAttachmentOld() }
a.addAction(getString(R.string.image_capture)) { performCamera() }
a.addAction(getString(R.string.image_capture)) {
performCamera()
}
a.addAction(getString(R.string.pick_images)) {
openAttachmentChooser(R.string.pick_images, "image/*", "video/*")
}
a.addAction(getString(R.string.pick_videos)) {
openAttachmentChooser(R.string.pick_videos, "video/*")
}
a.addAction(getString(R.string.pick_audios)) {
openAttachmentChooser(R.string.pick_audios, "audio/*")
}
// a.addAction( getString( R.string.video_capture ), new Runnable() {
// @Override public void run(){
@ -1603,15 +1677,10 @@ class ActPost : AppCompatActivity(),
}
private fun performAttachmentOld() {
private fun openAttachmentChooser(titleId : Int, vararg mimeTypes : String) {
// SAFのIntentで開く
try {
val intent = intentGetContent(
true,
getString(R.string.pick_images)
, "image/*"
, "video/*"
)
val intent = intentGetContent(true, getString(titleId), mimeTypes)
startActivityForResult(intent, REQUEST_CODE_ATTACHMENT_OLD)
} catch(ex : Throwable) {
log.trace(ex)
@ -1718,7 +1787,7 @@ class ActPost : AppCompatActivity(),
// image/j()pg だの image/j(e)pg だの、mime type を誤記するアプリがあまりに多い
// クレームで消耗するのを減らすためにファイルヘッダを確認する
if(mimeTypeArg == null || mimeTypeArg.startsWith("image/")) {
val sv = checkImageHeaderList(contentResolver, uri)
val sv = findMimeTypeByFileHeader(contentResolver, uri)
if(sv != null) return sv
}
@ -1861,7 +1930,7 @@ class ActPost : AppCompatActivity(),
val opener = createOpener(uri, mimeType)
val media_size_max = when {
mimeType.startsWith("video") -> {
mimeType.startsWith("video") || mimeType.startsWith("audio") -> {
1000000 * Math.max(1, Pref.spMovieSizeMax.toInt(pref))
}

View File

@ -2,6 +2,14 @@ package jp.juggler.subwaytooter.api.entity
interface TootAttachmentLike{
companion object {
const val TYPE_IMAGE = "image"
const val TYPE_VIDEO = "video"
const val TYPE_GIFV = "gifv"
const val TYPE_UNKNOWN = "unknown"
const val TYPE_AUDIO = "audio"
}
val type : String?
val description : String?
val urlForThumbnail : String?
@ -13,12 +21,8 @@ interface TootAttachmentLike{
fun getUrlString() :String?
companion object {
const val TYPE_IMAGE = "image"
const val TYPE_VIDEO = "video"
const val TYPE_GIFV = "gifv"
const val TYPE_UNKNOWN = "unknown"
const val TYPE_AUDIO = "audio"
}
val isAudio : Boolean
get()= type == TYPE_AUDIO
}

View File

@ -262,7 +262,7 @@ fun intentOpenDocument(mimeType : String) : Intent {
fun intentGetContent(
allowMultiple : Boolean,
caption : String,
vararg mimeTypes : String
mimeTypes : Array<out String>
) : Intent {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
</vector>

View File

@ -509,6 +509,8 @@
<string name="pick_image">画像を選択</string>
<string name="pick_images">画像を選択</string>
<string name="pick_videos">動画を選択</string>
<string name="pick_audios">音声を選択</string>
<string name="please_add_account">アカウントがありません。事前にアカウントの追加を行ってください</string>
<string name="please_donate">開発継続のために寄付をお願いします!</string>

View File

@ -302,6 +302,9 @@
<string name="column_background">Column background</string>
<string name="pick_image">Pick an image</string>
<string name="pick_images">Pick image(s)…</string>
<string name="pick_videos">Pick video(s)…</string>
<string name="pick_audios">Pick audio(s)…</string>
<string name="image_alpha">Image alpha</string>
<string name="image">Image</string>
<string name="column_header">Column header</string>