(Mastodon 開発版)音声の投稿に対応 f7f23b4a19
This commit is contained in:
parent
ac548cb582
commit
201a365401
|
@ -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>
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue