2018-01-04 19:52:25 +01:00
|
|
|
package jp.juggler.subwaytooter
|
|
|
|
|
|
|
|
import android.Manifest
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
import android.app.Activity
|
|
|
|
import android.app.Dialog
|
|
|
|
import android.content.ContentValues
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.SharedPreferences
|
|
|
|
import android.content.pm.PackageManager
|
|
|
|
import android.graphics.Bitmap
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.AsyncTask
|
|
|
|
import android.os.Build
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Handler
|
|
|
|
import android.provider.MediaStore
|
|
|
|
import android.provider.OpenableColumns
|
|
|
|
import android.support.v4.app.ActivityCompat
|
|
|
|
import android.support.v4.content.ContextCompat
|
|
|
|
import android.support.v7.app.AlertDialog
|
|
|
|
import android.support.v7.app.AppCompatActivity
|
|
|
|
import android.text.Editable
|
|
|
|
import android.text.TextWatcher
|
|
|
|
import android.text.method.LinkMovementMethod
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
|
|
|
import android.view.ViewTreeObserver
|
|
|
|
import android.widget.Button
|
|
|
|
import android.widget.CheckBox
|
|
|
|
import android.widget.EditText
|
|
|
|
import android.widget.ImageButton
|
|
|
|
import android.widget.ScrollView
|
|
|
|
import android.widget.TextView
|
|
|
|
import jp.juggler.subwaytooter.api.*
|
|
|
|
|
|
|
|
import org.apache.commons.io.IOUtils
|
|
|
|
import org.json.JSONArray
|
|
|
|
import org.json.JSONException
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
|
|
|
import java.io.File
|
|
|
|
import java.io.FileInputStream
|
|
|
|
import java.io.FileOutputStream
|
|
|
|
import java.io.IOException
|
|
|
|
import java.io.InputStream
|
|
|
|
import java.lang.ref.WeakReference
|
|
|
|
import java.util.ArrayList
|
|
|
|
import java.util.HashSet
|
|
|
|
import java.util.Locale
|
|
|
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.TootAttachment
|
|
|
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
2018-01-10 16:47:35 +01:00
|
|
|
import jp.juggler.subwaytooter.api.entity.parseItem
|
2018-01-13 07:38:32 +01:00
|
|
|
import jp.juggler.subwaytooter.dialog.*
|
2018-01-04 19:52:25 +01:00
|
|
|
import jp.juggler.subwaytooter.table.AcctColor
|
|
|
|
import jp.juggler.subwaytooter.table.PostDraft
|
|
|
|
import jp.juggler.subwaytooter.table.SavedAccount
|
|
|
|
import jp.juggler.subwaytooter.span.MyClickableSpan
|
|
|
|
import jp.juggler.subwaytooter.span.MyClickableSpanClickCallback
|
|
|
|
import jp.juggler.subwaytooter.util.*
|
|
|
|
import jp.juggler.subwaytooter.view.MyEditText
|
|
|
|
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
|
|
|
import okhttp3.MediaType
|
|
|
|
import okhttp3.MultipartBody
|
|
|
|
import okhttp3.Request
|
|
|
|
import okhttp3.RequestBody
|
|
|
|
import okio.BufferedSink
|
|
|
|
|
|
|
|
class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callback {
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
internal val log = LogCategory("ActPost")
|
|
|
|
|
|
|
|
internal const val EXTRA_POSTED_ACCT = "posted_acct"
|
|
|
|
internal const val EXTRA_POSTED_STATUS_ID = "posted_status_id"
|
|
|
|
|
|
|
|
internal const val KEY_ACCOUNT_DB_ID = "account_db_id"
|
|
|
|
internal const val KEY_REPLY_STATUS = "reply_status"
|
|
|
|
internal const val KEY_INITIAL_TEXT = "initial_text"
|
|
|
|
internal const val KEY_SENT_INTENT = "sent_intent"
|
|
|
|
|
|
|
|
internal const val KEY_ATTACHMENT_LIST = "attachment_list"
|
|
|
|
internal const val KEY_VISIBILITY = "visibility"
|
|
|
|
internal const val KEY_IN_REPLY_TO_ID = "in_reply_to_id"
|
|
|
|
internal const val KEY_IN_REPLY_TO_TEXT = "in_reply_to_text"
|
|
|
|
internal const val KEY_IN_REPLY_TO_IMAGE = "in_reply_to_image"
|
|
|
|
internal const val KEY_IN_REPLY_TO_URL = "in_reply_to_url"
|
|
|
|
|
|
|
|
private const val REQUEST_CODE_ATTACHMENT = 1
|
|
|
|
private const val REQUEST_CODE_CAMERA = 2
|
|
|
|
private const val REQUEST_CODE_MUSHROOM = 3
|
|
|
|
private const val REQUEST_CODE_VIDEO = 4
|
|
|
|
|
|
|
|
private const val PERMISSION_REQUEST_CODE = 1
|
|
|
|
|
|
|
|
internal const val MIME_TYPE_JPEG = "image/jpeg"
|
|
|
|
internal const val MIME_TYPE_PNG = "image/png"
|
|
|
|
|
|
|
|
internal val list_resize_max = intArrayOf(0, 640, 800, 1024, 1280, 1600, 2048)
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
internal val acceptable_mime_types : HashSet<String> by lazy {
|
|
|
|
val v = HashSet<String>()
|
|
|
|
|
|
|
|
//
|
|
|
|
v.add("image/*") // Android標準のギャラリーが image/* を出してくることがあるらしい
|
|
|
|
v.add("video/*") // Android標準のギャラリーが image/* を出してくることがあるらしい
|
|
|
|
//
|
|
|
|
v.add("image/jpeg")
|
|
|
|
v.add("image/png")
|
|
|
|
v.add("image/gif")
|
|
|
|
v.add("video/webm")
|
|
|
|
v.add("video/mp4")
|
|
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
// private void performCameraVideo(){
|
|
|
|
//
|
|
|
|
// try{
|
|
|
|
// Intent takeVideoIntent = new Intent( MediaStore.ACTION_VIDEO_CAPTURE );
|
|
|
|
// startActivityForResult( takeVideoIntent, REQUEST_CODE_VIDEO );
|
|
|
|
// }catch( Throwable ex ){
|
|
|
|
// log.trace( ex );
|
|
|
|
// Utils.showToast( this, ex, "opening video app failed." );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
|
|
|
|
|
|
const val DRAFT_CONTENT = "content"
|
|
|
|
const val DRAFT_CONTENT_WARNING = "content_warning"
|
|
|
|
internal const val DRAFT_CONTENT_WARNING_CHECK = "content_warning_check"
|
|
|
|
internal const val DRAFT_NSFW_CHECK = "nsfw_check"
|
|
|
|
internal const val DRAFT_VISIBILITY = "visibility"
|
|
|
|
internal const val DRAFT_ACCOUNT_DB_ID = "account_db_id"
|
|
|
|
internal const val DRAFT_ATTACHMENT_LIST = "attachment_list"
|
|
|
|
internal const val DRAFT_REPLY_ID = "reply_id"
|
|
|
|
internal const val DRAFT_REPLY_TEXT = "reply_text"
|
|
|
|
internal const val DRAFT_REPLY_IMAGE = "reply_image"
|
|
|
|
internal const val DRAFT_REPLY_URL = "reply_url"
|
|
|
|
internal const val DRAFT_IS_ENQUETE = "is_enquete"
|
|
|
|
internal const val DRAFT_ENQUETE_ITEMS = "enquete_items"
|
|
|
|
|
|
|
|
private const val STATE_MUSHROOM_INPUT = "mushroom_input"
|
|
|
|
private const val STATE_MUSHROOM_START = "mushroom_start"
|
|
|
|
private const val STATE_MUSHROOM_END = "mushroom_end"
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
fun open(
|
|
|
|
activity : Activity,
|
|
|
|
request_code : Int,
|
|
|
|
account_db_id : Long,
|
|
|
|
reply_status : TootStatus?
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
val intent = Intent(activity, ActPost::class.java)
|
|
|
|
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
|
|
|
if(reply_status != null) {
|
|
|
|
intent.putExtra(KEY_REPLY_STATUS, reply_status.json.toString())
|
|
|
|
}
|
|
|
|
activity.startActivityForResult(intent, request_code)
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
fun open(
|
|
|
|
activity : Activity,
|
|
|
|
request_code : Int,
|
|
|
|
account_db_id : Long,
|
|
|
|
initial_text : String?
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
val intent = Intent(activity, ActPost::class.java)
|
|
|
|
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
|
|
|
if(initial_text != null) {
|
|
|
|
intent.putExtra(KEY_INITIAL_TEXT, initial_text)
|
|
|
|
}
|
|
|
|
activity.startActivityForResult(intent, request_code)
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
fun open(
|
|
|
|
activity : Activity,
|
|
|
|
request_code : Int,
|
|
|
|
account_db_id : Long,
|
|
|
|
sent_intent : Intent?
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
val intent = Intent(activity, ActPost::class.java)
|
|
|
|
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
|
|
|
if(sent_intent != null) {
|
|
|
|
intent.putExtra(KEY_SENT_INTENT, sent_intent)
|
|
|
|
}
|
|
|
|
activity.startActivityForResult(intent, request_code)
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun check_exist(url : String?) : Boolean {
|
2018-01-21 13:46:36 +01:00
|
|
|
if(url?.isEmpty() != false) return false
|
2018-01-04 19:52:25 +01:00
|
|
|
try {
|
2018-01-21 13:46:36 +01:00
|
|
|
val request = Request.Builder().url(url).build()
|
2018-01-04 19:52:25 +01:00
|
|
|
val call = App1.ok_http_client.newCall(request)
|
|
|
|
val response = call.execute()
|
|
|
|
if(response.isSuccessful) {
|
|
|
|
return true
|
|
|
|
}
|
2018-01-12 10:01:25 +01:00
|
|
|
log.e(TootApiClient.formatResponse(response, "check_exist failed."))
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private lateinit var btnAccount : Button
|
|
|
|
private lateinit var btnVisibility : ImageButton
|
|
|
|
private lateinit var btnAttachment : View
|
|
|
|
private lateinit var btnPost : View
|
|
|
|
private lateinit var llAttachment : View
|
|
|
|
private lateinit var ivMedia : List<MyNetworkImageView>
|
|
|
|
internal lateinit var cbNSFW : CheckBox
|
|
|
|
internal lateinit var cbContentWarning : CheckBox
|
|
|
|
internal lateinit var etContentWarning : MyEditText
|
|
|
|
internal lateinit var etContent : MyEditText
|
|
|
|
|
|
|
|
internal lateinit var cbEnquete : CheckBox
|
|
|
|
private lateinit var llEnquete : View
|
|
|
|
internal lateinit var list_etChoice : List<MyEditText>
|
|
|
|
|
|
|
|
private lateinit var tvCharCount : TextView
|
|
|
|
internal lateinit var handler : Handler
|
|
|
|
private lateinit var formRoot : View
|
|
|
|
|
|
|
|
private lateinit var llReply : View
|
|
|
|
private lateinit var tvReplyTo : TextView
|
|
|
|
private lateinit var btnRemoveReply : View
|
|
|
|
private lateinit var ivReply : MyNetworkImageView
|
2018-01-10 16:47:35 +01:00
|
|
|
private lateinit var scrollView : ScrollView
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
internal lateinit var pref : SharedPreferences
|
|
|
|
internal lateinit var app_state : AppState
|
|
|
|
private lateinit var post_helper : PostHelper
|
|
|
|
internal var attachment_list = ArrayList<PostAttachment>()
|
|
|
|
private var isPostComplete : Boolean = false
|
|
|
|
|
|
|
|
internal var density : Float = 0f
|
|
|
|
|
|
|
|
private lateinit var account_list : ArrayList<SavedAccount>
|
|
|
|
|
|
|
|
private val text_watcher : TextWatcher = object : TextWatcher {
|
|
|
|
override fun beforeTextChanged(charSequence : CharSequence, i : Int, i1 : Int, i2 : Int) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onTextChanged(charSequence : CharSequence, i : Int, i1 : Int, i2 : Int) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun afterTextChanged(editable : Editable) {
|
|
|
|
updateTextCount()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
private val scroll_listener : ViewTreeObserver.OnScrollChangedListener =
|
|
|
|
ViewTreeObserver.OnScrollChangedListener { post_helper.onScrollChanged() }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// Account
|
|
|
|
|
|
|
|
internal var account : SavedAccount? = null
|
|
|
|
|
|
|
|
private var uriCameraImage : Uri? = null
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// visibility
|
|
|
|
|
|
|
|
internal var visibility : String? = null
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
|
|
|
|
|
|
internal var in_reply_to_id = - 1L
|
|
|
|
internal var in_reply_to_text : String? = null
|
|
|
|
internal var in_reply_to_image : String? = null
|
|
|
|
internal var in_reply_to_url : String? = null
|
|
|
|
private var mushroom_input : Int = 0
|
|
|
|
private var mushroom_start : Int = 0
|
|
|
|
private var mushroom_end : Int = 0
|
|
|
|
|
|
|
|
private val link_click_listener : MyClickableSpanClickCallback = { _, span ->
|
|
|
|
try {
|
|
|
|
// ブラウザで開く
|
|
|
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(span.url))
|
|
|
|
startActivity(intent)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
override fun onClick(v : View) {
|
|
|
|
when(v.id) {
|
|
|
|
R.id.btnAccount -> performAccountChooser()
|
|
|
|
R.id.btnVisibility -> performVisibility()
|
|
|
|
R.id.btnAttachment -> openAttachment()
|
|
|
|
R.id.ivMedia1 -> performAttachmentClick(0)
|
|
|
|
R.id.ivMedia2 -> performAttachmentClick(1)
|
|
|
|
R.id.ivMedia3 -> performAttachmentClick(2)
|
|
|
|
R.id.ivMedia4 -> performAttachmentClick(3)
|
|
|
|
R.id.btnPost -> performPost()
|
|
|
|
R.id.btnRemoveReply -> removeReply()
|
|
|
|
R.id.btnMore -> performMore()
|
|
|
|
R.id.btnPlugin -> openMushroom()
|
2018-01-14 22:47:42 +01:00
|
|
|
R.id.btnEmojiPicker -> post_helper.openEmojiPickerFromMore()
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
|
|
|
|
if(requestCode == REQUEST_CODE_ATTACHMENT && resultCode == Activity.RESULT_OK) {
|
|
|
|
if(data != null) {
|
|
|
|
var uri = data.data
|
|
|
|
if(uri != null) {
|
|
|
|
// 単一選択
|
|
|
|
var type = data.type
|
2018-01-21 13:46:36 +01:00
|
|
|
if(type?.isEmpty() != false) {
|
2018-01-04 19:52:25 +01:00
|
|
|
type = contentResolver.getType(uri)
|
|
|
|
}
|
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
val cd = data.clipData
|
|
|
|
if(cd != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
// 複数選択
|
2018-01-04 19:52:25 +01:00
|
|
|
val count = cd.itemCount
|
|
|
|
for(i in 0 until count) {
|
|
|
|
val item = cd.getItemAt(i)
|
2018-01-10 16:47:35 +01:00
|
|
|
uri = item.uri ?: continue
|
|
|
|
val type = contentResolver.getType(uri)
|
2018-01-04 19:52:25 +01:00
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if(requestCode == REQUEST_CODE_CAMERA) {
|
|
|
|
|
|
|
|
if(resultCode != Activity.RESULT_OK) {
|
|
|
|
// 失敗したら DBからデータを削除
|
2018-01-10 16:47:35 +01:00
|
|
|
val uriCameraImage = this.uriCameraImage
|
2018-01-21 13:46:36 +01:00
|
|
|
if(uriCameraImage != null) {
|
|
|
|
contentResolver.delete(uriCameraImage, null, null)
|
2018-01-10 16:47:35 +01:00
|
|
|
this@ActPost.uriCameraImage = null
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 画像のURL
|
2018-01-10 16:47:35 +01:00
|
|
|
val uri : Uri? = data?.data ?: uriCameraImage
|
2018-01-04 19:52:25 +01:00
|
|
|
if(uri != null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val type : String? = contentResolver.getType(uri)
|
2018-01-04 19:52:25 +01:00
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if(requestCode == REQUEST_CODE_VIDEO && resultCode == Activity.RESULT_OK) {
|
|
|
|
val uri = data?.data
|
|
|
|
if(uri != null) {
|
|
|
|
val type = contentResolver.getType(uri)
|
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if(requestCode == REQUEST_CODE_MUSHROOM && resultCode == Activity.RESULT_OK) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val text = data?.getStringExtra("replace_key")
|
2018-01-21 13:46:36 +01:00
|
|
|
if(text != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
applyMushroomResult(text)
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBackPressed() {
|
|
|
|
saveDraft()
|
|
|
|
super.onBackPressed()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
MyClickableSpan.link_callback = WeakReference(link_click_listener)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPause() {
|
|
|
|
super.onPause()
|
|
|
|
|
|
|
|
// 編集中にホーム画面を押したり他アプリに移動する場合は下書きを保存する
|
|
|
|
// やや過剰な気がするが、自アプリに戻ってくるときにランチャーからアイコンタップされると
|
|
|
|
// メイン画面より上にあるアクティビティはすべて消されてしまうので
|
|
|
|
// このタイミングで保存するしかない
|
|
|
|
if(! isPostComplete) {
|
|
|
|
saveDraft()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState : Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
App1.setActivityTheme(this, true)
|
|
|
|
|
|
|
|
app_state = App1.getAppState(this)
|
|
|
|
pref = app_state.pref
|
|
|
|
|
|
|
|
initUI()
|
|
|
|
|
|
|
|
if(account_list.isEmpty()) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.please_add_account)
|
2018-01-04 19:52:25 +01:00
|
|
|
finish()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(savedInstanceState != null) {
|
|
|
|
|
|
|
|
mushroom_input = savedInstanceState.getInt(STATE_MUSHROOM_INPUT, 0)
|
|
|
|
mushroom_start = savedInstanceState.getInt(STATE_MUSHROOM_START, 0)
|
|
|
|
mushroom_end = savedInstanceState.getInt(STATE_MUSHROOM_END, 0)
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
val account_db_id =
|
|
|
|
savedInstanceState.getLong(KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_DB_ID)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(account_db_id != SavedAccount.INVALID_DB_ID) {
|
|
|
|
var i = 0
|
|
|
|
val ie = account_list.size
|
|
|
|
while(i < ie) {
|
|
|
|
val a = account_list[i]
|
|
|
|
if(a.db_id == account_db_id) {
|
|
|
|
selectAccount(a)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.visibility = savedInstanceState.getString(KEY_VISIBILITY)
|
|
|
|
|
|
|
|
val sv = savedInstanceState.getString(KEY_ATTACHMENT_LIST)
|
|
|
|
|
|
|
|
if(app_state.attachment_list != null) {
|
|
|
|
|
|
|
|
val list_in_state = app_state.attachment_list
|
|
|
|
if(list_in_state != null) {
|
|
|
|
// static なデータが残ってるならそれを使う
|
|
|
|
this.attachment_list = list_in_state
|
|
|
|
}
|
|
|
|
|
|
|
|
// コールバックを新しい画面に差し替える
|
|
|
|
for(pa in attachment_list) {
|
|
|
|
pa.callback = this
|
|
|
|
}
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
} else if(sv != null && sv.isNotEmpty()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
// state から復元する
|
|
|
|
app_state.attachment_list = this.attachment_list
|
|
|
|
this.attachment_list.clear()
|
|
|
|
|
|
|
|
try {
|
2018-01-21 13:46:36 +01:00
|
|
|
val array = sv.toJsonArray()
|
|
|
|
for( i in 0 until array.length()){
|
2018-01-04 19:52:25 +01:00
|
|
|
try {
|
2018-01-10 16:47:35 +01:00
|
|
|
val a = parseItem(::TootAttachment, array.optJSONObject(i))
|
2018-01-21 13:46:36 +01:00
|
|
|
if(a != null) attachment_list.add(PostAttachment(a))
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this.in_reply_to_id = savedInstanceState.getLong(KEY_IN_REPLY_TO_ID, - 1L)
|
|
|
|
this.in_reply_to_text = savedInstanceState.getString(KEY_IN_REPLY_TO_TEXT)
|
|
|
|
this.in_reply_to_image = savedInstanceState.getString(KEY_IN_REPLY_TO_IMAGE)
|
|
|
|
this.in_reply_to_url = savedInstanceState.getString(KEY_IN_REPLY_TO_URL)
|
|
|
|
} else {
|
|
|
|
app_state.attachment_list = this.attachment_list
|
|
|
|
this.attachment_list.clear()
|
|
|
|
|
|
|
|
val intent = intent
|
|
|
|
val account_db_id = intent.getLongExtra(KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_DB_ID)
|
|
|
|
if(account_db_id != SavedAccount.INVALID_DB_ID) {
|
|
|
|
var i = 0
|
|
|
|
val ie = account_list.size
|
|
|
|
while(i < ie) {
|
|
|
|
val a = account_list[i]
|
|
|
|
if(a.db_id == account_db_id) {
|
|
|
|
selectAccount(a)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val sent_intent = intent.getParcelableExtra<Intent>(KEY_SENT_INTENT)
|
|
|
|
if(sent_intent != null) {
|
|
|
|
val action = sent_intent.action
|
|
|
|
val type = sent_intent.type
|
|
|
|
|
|
|
|
if(type == null) {
|
|
|
|
//
|
|
|
|
} else if(type.startsWith("image/") || type.startsWith("video/")) {
|
|
|
|
|
|
|
|
if(Intent.ACTION_VIEW == action) {
|
|
|
|
val uri = sent_intent.data
|
|
|
|
if(uri != null) {
|
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
} else if(Intent.ACTION_SEND == action) {
|
|
|
|
val uri = sent_intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
|
|
|
if(uri != null) {
|
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
} else if(Intent.ACTION_SEND_MULTIPLE == action) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val list_uri =
|
|
|
|
sent_intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(list_uri != null) {
|
|
|
|
for(uri in list_uri) {
|
|
|
|
if(uri != null) {
|
|
|
|
addAttachment(uri, type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if(type.startsWith("text/")) {
|
|
|
|
if(Intent.ACTION_SEND == action) {
|
|
|
|
val sv = sent_intent.getStringExtra(Intent.EXTRA_TEXT)
|
|
|
|
if(sv != null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this, sv)
|
2018-01-16 07:48:17 +01:00
|
|
|
etContent.setText(svEmoji)
|
|
|
|
etContent.setSelection(svEmoji.length)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var sv : String? = intent.getStringExtra(KEY_INITIAL_TEXT)
|
|
|
|
if(sv != null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this, sv)
|
2018-01-16 07:48:17 +01:00
|
|
|
etContent.setText(svEmoji)
|
|
|
|
etContent.setSelection(svEmoji.length)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
val account = this.account
|
2018-01-10 16:47:35 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
sv = intent.getStringExtra(KEY_REPLY_STATUS)
|
2018-01-10 16:47:35 +01:00
|
|
|
if(sv != null && account != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
try {
|
2018-01-21 13:46:36 +01:00
|
|
|
val reply_status = TootParser(this@ActPost, account).status(sv.toJsonObject())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
if(reply_status != null) {
|
|
|
|
// CW をリプライ元に合わせる
|
2018-01-10 16:47:35 +01:00
|
|
|
if(reply_status.spoiler_text?.isNotEmpty() == true) {
|
2018-01-04 19:52:25 +01:00
|
|
|
cbContentWarning.isChecked = true
|
|
|
|
etContentWarning.setText(reply_status.spoiler_text)
|
|
|
|
}
|
|
|
|
|
|
|
|
val mention_list = ArrayList<String>()
|
|
|
|
|
|
|
|
// 元レスにあった mention
|
|
|
|
val old_mentions = reply_status.mentions
|
|
|
|
if(old_mentions != null) {
|
|
|
|
for(mention in old_mentions) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val who_acct = mention.acct
|
|
|
|
if(who_acct.isNotEmpty()) {
|
|
|
|
if(account.isMe(who_acct)) continue
|
|
|
|
sv = "@" + account.getFullAcct(who_acct)
|
|
|
|
if(! mention_list.contains(sv)) {
|
|
|
|
mention_list.add(sv)
|
|
|
|
}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 今回メンションを追加する?
|
2018-01-10 16:47:35 +01:00
|
|
|
val who_acct = account.getFullAcct(reply_status.account)
|
2018-01-21 13:46:36 +01:00
|
|
|
if(mention_list.contains("@" + who_acct)) {
|
2018-01-10 16:47:35 +01:00
|
|
|
// 既に含まれている
|
|
|
|
} else if(! account.isMe(reply_status.account) || mention_list.isEmpty()) {
|
|
|
|
// 自分ではない、もしくは、メンションが空
|
|
|
|
mention_list.add("@" + who_acct)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
val sb = StringBuilder()
|
|
|
|
for(acct in mention_list) {
|
|
|
|
if(sb.isNotEmpty()) sb.append(' ')
|
|
|
|
sb.append(acct)
|
|
|
|
}
|
|
|
|
if(sb.isNotEmpty()) {
|
|
|
|
sb.append(' ')
|
2018-01-21 13:46:36 +01:00
|
|
|
val svEmoji =
|
|
|
|
DecodeOptions(decodeEmoji = true).decodeEmoji(this, sb.toString())
|
2018-01-16 07:48:17 +01:00
|
|
|
etContent.setText(svEmoji)
|
|
|
|
etContent.setSelection(svEmoji.length)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// リプライ表示をつける
|
|
|
|
in_reply_to_id = reply_status.id
|
|
|
|
in_reply_to_text = reply_status.content
|
|
|
|
in_reply_to_image = reply_status.account.avatar_static
|
|
|
|
in_reply_to_url = reply_status.url
|
|
|
|
|
|
|
|
// 公開範囲
|
|
|
|
try {
|
|
|
|
// 比較する前にデフォルトの公開範囲を計算する
|
2018-01-10 16:47:35 +01:00
|
|
|
visibility = when {
|
|
|
|
visibility?.isNotEmpty() == true -> visibility
|
|
|
|
account.visibility?.isNotEmpty() == true -> account.visibility
|
|
|
|
else -> TootStatus.VISIBILITY_PUBLIC
|
|
|
|
// VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになる
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(TootStatus.VISIBILITY_WEB_SETTING == visibility) {
|
|
|
|
// 「Web設定に合わせる」だった場合は無条件にリプライ元の公開範囲に変更する
|
|
|
|
this.visibility = reply_status.visibility
|
|
|
|
} else {
|
|
|
|
// デフォルトの方が公開範囲が大きい場合、リプライ元に合わせて公開範囲を狭める
|
2018-01-21 13:46:36 +01:00
|
|
|
if(TootStatus.isVisibilitySpoilRequired(
|
|
|
|
this.visibility,
|
|
|
|
reply_status.visibility
|
|
|
|
)) {
|
2018-01-04 19:52:25 +01:00
|
|
|
this.visibility = reply_status.visibility
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
visibility = when {
|
|
|
|
visibility?.isNotEmpty() == true -> visibility
|
|
|
|
account?.visibility?.isNotEmpty() == true -> account?.visibility
|
|
|
|
else -> TootStatus.VISIBILITY_PUBLIC
|
|
|
|
|
|
|
|
// 2017/9/13 VISIBILITY_WEB_SETTING から VISIBILITY_PUBLICに変更した
|
|
|
|
// VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになるので…
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(this.account == null) {
|
|
|
|
// 表示を未選択に更新
|
|
|
|
selectAccount(null)
|
|
|
|
}
|
|
|
|
|
|
|
|
updateContentWarning()
|
|
|
|
showMediaAttachment()
|
|
|
|
showVisibility()
|
|
|
|
updateTextCount()
|
|
|
|
showReplyTo()
|
|
|
|
showEnquete()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
post_helper.onDestroy()
|
|
|
|
|
|
|
|
super.onDestroy()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSaveInstanceState(outState : Bundle?) {
|
|
|
|
super.onSaveInstanceState(outState)
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
outState ?: return
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
outState.putInt(STATE_MUSHROOM_INPUT, mushroom_input)
|
2018-01-04 19:52:25 +01:00
|
|
|
outState.putInt(STATE_MUSHROOM_START, mushroom_start)
|
|
|
|
outState.putInt(STATE_MUSHROOM_END, mushroom_end)
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
val account = this.account
|
2018-01-04 19:52:25 +01:00
|
|
|
if(account != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
outState.putLong(KEY_ACCOUNT_DB_ID, account.db_id)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(visibility != null) {
|
|
|
|
outState.putString(KEY_VISIBILITY, visibility)
|
|
|
|
}
|
|
|
|
|
|
|
|
if(! attachment_list.isEmpty()) {
|
|
|
|
val array = JSONArray()
|
|
|
|
for(pa in attachment_list) {
|
|
|
|
if(pa.status == PostAttachment.STATUS_UPLOADED) {
|
|
|
|
// アップロード完了したものだけ保持する
|
|
|
|
array.put(pa.attachment?.json)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outState.putString(KEY_ATTACHMENT_LIST, array.toString())
|
|
|
|
}
|
|
|
|
|
|
|
|
outState.putLong(KEY_IN_REPLY_TO_ID, in_reply_to_id)
|
|
|
|
outState.putString(KEY_IN_REPLY_TO_TEXT, in_reply_to_text)
|
|
|
|
outState.putString(KEY_IN_REPLY_TO_IMAGE, in_reply_to_image)
|
|
|
|
outState.putString(KEY_IN_REPLY_TO_URL, in_reply_to_url)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRestoreInstanceState(savedInstanceState : Bundle) {
|
|
|
|
super.onRestoreInstanceState(savedInstanceState)
|
|
|
|
updateContentWarning()
|
|
|
|
showMediaAttachment()
|
|
|
|
showVisibility()
|
|
|
|
updateTextCount()
|
|
|
|
showReplyTo()
|
|
|
|
showEnquete()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun initUI() {
|
|
|
|
handler = Handler()
|
|
|
|
density = resources.displayMetrics.density
|
|
|
|
|
|
|
|
setContentView(R.layout.act_post)
|
|
|
|
|
2018-01-17 02:16:26 +01:00
|
|
|
if(Pref.bpPostButtonBarTop(this)) {
|
2018-01-04 19:52:25 +01:00
|
|
|
val bar = findViewById<View>(R.id.llFooterBar)
|
|
|
|
val parent = bar.parent as ViewGroup
|
|
|
|
parent.removeView(bar)
|
|
|
|
parent.addView(bar, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
Styler.fixHorizontalMargin(findViewById(R.id.scrollView))
|
|
|
|
Styler.fixHorizontalMargin(findViewById(R.id.llFooterBar))
|
|
|
|
|
|
|
|
formRoot = findViewById(R.id.viewRoot)
|
|
|
|
scrollView = findViewById(R.id.scrollView)
|
|
|
|
btnAccount = findViewById(R.id.btnAccount)
|
|
|
|
btnVisibility = findViewById(R.id.btnVisibility)
|
|
|
|
btnAttachment = findViewById(R.id.btnAttachment)
|
|
|
|
btnPost = findViewById(R.id.btnPost)
|
|
|
|
llAttachment = findViewById(R.id.llAttachment)
|
|
|
|
|
|
|
|
cbNSFW = findViewById(R.id.cbNSFW)
|
|
|
|
cbContentWarning = findViewById(R.id.cbContentWarning)
|
|
|
|
etContentWarning = findViewById(R.id.etContentWarning)
|
|
|
|
etContent = findViewById(R.id.etContent)
|
|
|
|
|
|
|
|
cbEnquete = findViewById(R.id.cbEnquete)
|
|
|
|
llEnquete = findViewById(R.id.llEnquete)
|
|
|
|
|
|
|
|
ivMedia = listOf(
|
|
|
|
findViewById(R.id.ivMedia1),
|
|
|
|
findViewById(R.id.ivMedia2),
|
|
|
|
findViewById(R.id.ivMedia3),
|
|
|
|
findViewById(R.id.ivMedia4)
|
|
|
|
)
|
|
|
|
|
|
|
|
list_etChoice = listOf(
|
|
|
|
findViewById(R.id.etChoice1),
|
|
|
|
findViewById(R.id.etChoice2),
|
|
|
|
findViewById(R.id.etChoice3),
|
|
|
|
findViewById(R.id.etChoice4)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
tvCharCount = findViewById(R.id.tvCharCount)
|
|
|
|
|
|
|
|
llReply = findViewById(R.id.llReply)
|
|
|
|
tvReplyTo = findViewById(R.id.tvReplyTo)
|
|
|
|
btnRemoveReply = findViewById(R.id.btnRemoveReply)
|
|
|
|
ivReply = findViewById(R.id.ivReply)
|
|
|
|
|
|
|
|
account_list = SavedAccount.loadAccountList(this@ActPost)
|
|
|
|
SavedAccount.sort(account_list)
|
|
|
|
|
|
|
|
btnAccount.setOnClickListener(this)
|
|
|
|
btnVisibility.setOnClickListener(this)
|
|
|
|
btnAttachment.setOnClickListener(this)
|
|
|
|
btnPost.setOnClickListener(this)
|
|
|
|
btnRemoveReply.setOnClickListener(this)
|
|
|
|
|
|
|
|
findViewById<View>(R.id.btnPlugin).setOnClickListener(this)
|
2018-01-14 22:47:42 +01:00
|
|
|
findViewById<View>(R.id.btnEmojiPicker).setOnClickListener(this)
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
for(iv in ivMedia) {
|
|
|
|
iv.setOnClickListener(this)
|
|
|
|
iv.setDefaultImageResId(Styler.getAttributeResourceId(this, R.attr.ic_loading))
|
|
|
|
iv.setErrorImageResId(Styler.getAttributeResourceId(this, R.attr.ic_unknown))
|
|
|
|
}
|
|
|
|
|
|
|
|
cbContentWarning.setOnCheckedChangeListener { _, _ ->
|
|
|
|
updateContentWarning()
|
|
|
|
}
|
|
|
|
|
|
|
|
cbEnquete.setOnCheckedChangeListener { _, _ ->
|
|
|
|
showEnquete()
|
|
|
|
updateTextCount()
|
|
|
|
}
|
|
|
|
|
|
|
|
post_helper = PostHelper(this, pref, app_state.handler)
|
|
|
|
post_helper.attachEditText(formRoot, etContent, false, object : PostHelper.Callback2 {
|
|
|
|
override fun onTextUpdate() {
|
|
|
|
updateTextCount()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun canOpenPopup() : Boolean {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
etContentWarning.addTextChangedListener(text_watcher)
|
|
|
|
for(et in list_etChoice) {
|
|
|
|
et.addTextChangedListener(text_watcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollView.viewTreeObserver.addOnScrollChangedListener(scroll_listener)
|
|
|
|
|
|
|
|
val v = findViewById<View>(R.id.btnMore)
|
|
|
|
v.setOnClickListener(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun updateTextCount() {
|
|
|
|
var length = 0
|
|
|
|
|
|
|
|
var s = EmojiDecoder.decodeShortCode(etContent.text.toString())
|
|
|
|
length += s.codePointCount(0, s.length)
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
s =
|
|
|
|
if(cbContentWarning.isChecked) EmojiDecoder.decodeShortCode(etContentWarning.text.toString()) else ""
|
2018-01-04 19:52:25 +01:00
|
|
|
length += s.codePointCount(0, s.length)
|
|
|
|
|
|
|
|
val max : Int
|
|
|
|
if(! cbEnquete.isChecked) {
|
|
|
|
max = 500
|
|
|
|
} else {
|
|
|
|
max = 350
|
|
|
|
for(et in list_etChoice) {
|
|
|
|
s = EmojiDecoder.decodeShortCode(et.text.toString())
|
|
|
|
length += s.codePointCount(0, s.length)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val remain = max - length
|
|
|
|
tvCharCount.text = Integer.toString(remain)
|
2018-01-21 13:46:36 +01:00
|
|
|
val color = Styler.getAttributeColor(
|
|
|
|
this,
|
|
|
|
if(remain < 0) R.attr.colorRegexFilterError else android.R.attr.textColorPrimary
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
tvCharCount.setTextColor(color)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun updateContentWarning() {
|
|
|
|
etContentWarning.visibility = if(cbContentWarning.isChecked) View.VISIBLE else View.GONE
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun selectAccount(a : SavedAccount?) {
|
|
|
|
this.account = a
|
|
|
|
if(a == null) {
|
|
|
|
post_helper.setInstance(null)
|
|
|
|
btnAccount.text = getString(R.string.not_selected)
|
|
|
|
btnAccount.setTextColor(Styler.getAttributeColor(this, android.R.attr.textColorPrimary))
|
|
|
|
btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent)
|
|
|
|
} else {
|
|
|
|
post_helper.setInstance(a.host)
|
2018-01-10 16:47:35 +01:00
|
|
|
val acct = a.acct
|
2018-01-04 19:52:25 +01:00
|
|
|
val ac = AcctColor.load(acct)
|
|
|
|
val nickname = if(AcctColor.hasNickname(ac)) ac.nickname else acct
|
|
|
|
btnAccount.text = nickname
|
|
|
|
|
|
|
|
if(AcctColor.hasColorBackground(ac)) {
|
|
|
|
btnAccount.setBackgroundColor(ac.color_bg)
|
|
|
|
} else {
|
|
|
|
btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent)
|
|
|
|
}
|
|
|
|
if(AcctColor.hasColorForeground(ac)) {
|
|
|
|
btnAccount.setTextColor(ac.color_fg)
|
|
|
|
} else {
|
2018-01-21 13:46:36 +01:00
|
|
|
btnAccount.setTextColor(
|
|
|
|
Styler.getAttributeColor(
|
|
|
|
this,
|
|
|
|
android.R.attr.textColorPrimary
|
|
|
|
)
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun performAccountChooser() {
|
|
|
|
|
|
|
|
if(! attachment_list.isEmpty()) {
|
|
|
|
// 添付ファイルがあったら確認の上添付ファイルを捨てないと切り替えられない
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.cant_change_account_when_attachment_specified)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-13 07:15:52 +01:00
|
|
|
AccountPicker.pick(
|
|
|
|
this,
|
|
|
|
bAllowPseudo = false,
|
|
|
|
bAuto = false,
|
|
|
|
message = getString(R.string.choose_account)
|
|
|
|
) { ai ->
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
// 別タンスのアカウントに変更したならならin_reply_toの変換が必要
|
|
|
|
if(in_reply_to_id != - 1L && ! ai.host.equals(account?.host, ignoreCase = true)) {
|
|
|
|
startReplyConversion(ai)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
setAccountWithVisibilityConversion(ai)
|
|
|
|
}
|
|
|
|
|
|
|
|
// final ArrayList< SavedAccount > tmp_account_list = new ArrayList<>();
|
|
|
|
// tmp_account_list.addAll( account_list );
|
|
|
|
//
|
|
|
|
// String[] caption_list = new String[ tmp_account_list.size() ];
|
|
|
|
// for( int i = 0, ie = tmp_account_list.size() ; i < ie ; ++ i ){
|
|
|
|
// caption_list[ i ] = tmp_account_list.get( i ).acct;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// new AlertDialog.Builder( this )
|
|
|
|
// .setTitle( R.string.choose_account )
|
|
|
|
// .setItems( caption_list, new DialogInterface.OnClickListener() {
|
|
|
|
// @Override
|
|
|
|
// public void onClick( DialogInterface dialog, int which ){
|
|
|
|
//
|
|
|
|
// if( which < 0 || which >= tmp_account_list.size() ){
|
|
|
|
// // 範囲外
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// SavedAccount ai = tmp_account_list.get( which );
|
|
|
|
//
|
|
|
|
// if( ! ai.host.equals( account.host ) ){
|
|
|
|
// // 別タンスへの移動
|
|
|
|
// if( in_reply_to_id != - 1L ){
|
|
|
|
// // 別タンスのアカウントならin_reply_toの変換が必要
|
|
|
|
// startReplyConversion( ai );
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // リプライがないか、同タンスへの移動
|
|
|
|
// setAccountWithVisibilityConversion( ai );
|
|
|
|
// }
|
|
|
|
// } )
|
|
|
|
// .setNegativeButton( R.string.cancel, null )
|
|
|
|
// .show();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun setAccountWithVisibilityConversion(a : SavedAccount) {
|
|
|
|
selectAccount(a)
|
|
|
|
try {
|
|
|
|
if(TootStatus.isVisibilitySpoilRequired(this.visibility, a.visibility)) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActPost, true, R.string.spoil_visibility_for_account)
|
2018-01-04 19:52:25 +01:00
|
|
|
this.visibility = a.visibility
|
|
|
|
showVisibility()
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressLint("StaticFieldLeak")
|
|
|
|
private fun startReplyConversion(access_info : SavedAccount) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val in_reply_to_url = this.in_reply_to_url
|
2018-01-04 19:52:25 +01:00
|
|
|
if(in_reply_to_url == null) {
|
|
|
|
// 下書きが古い形式の場合、URLがないので別タンスへの移動ができない
|
|
|
|
AlertDialog.Builder(this@ActPost)
|
|
|
|
.setMessage(R.string.account_change_failed_old_draft_has_no_in_reply_to_url)
|
|
|
|
.setNeutralButton(R.string.close, null)
|
|
|
|
.show()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
TootTaskRunner(this)
|
2018-01-04 19:52:25 +01:00
|
|
|
.progressPrefix(getString(R.string.progress_synchronize_toot))
|
|
|
|
.run(access_info, object : TootTask {
|
|
|
|
|
|
|
|
internal var target_status : TootStatus? = null
|
|
|
|
override fun background(client : TootApiClient) : TootApiResult? {
|
|
|
|
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
2018-01-21 13:46:36 +01:00
|
|
|
val path = String.format(
|
|
|
|
Locale.JAPAN,
|
|
|
|
Column.PATH_SEARCH,
|
|
|
|
in_reply_to_url.encodePercent()
|
|
|
|
) + "&resolve=1"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val result = client.request(path)
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
if(jsonObject != null) {
|
|
|
|
val tmp = TootParser(this@ActPost, access_info).results(jsonObject)
|
|
|
|
if(tmp?.statuses != null && ! tmp.statuses.isEmpty()) {
|
|
|
|
target_status = tmp.statuses[0]
|
|
|
|
}
|
|
|
|
if(target_status == null) {
|
|
|
|
return TootApiResult(getString(R.string.status_id_conversion_failed))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun handleResult(result : TootApiResult?) {
|
|
|
|
if(result == null) return // cancelled.
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
val target_status = this.target_status
|
2018-01-04 19:52:25 +01:00
|
|
|
if(target_status != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
in_reply_to_id = target_status.id
|
2018-01-04 19:52:25 +01:00
|
|
|
setAccountWithVisibilityConversion(access_info)
|
|
|
|
} else {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(
|
|
|
|
this@ActPost,
|
|
|
|
true,
|
|
|
|
getString(R.string.in_reply_to_id_conversion_failed) + "\n" + result.error
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// Attachment
|
|
|
|
|
|
|
|
private fun showMediaAttachment() {
|
|
|
|
|
|
|
|
if(isFinishing) return
|
|
|
|
|
|
|
|
if(attachment_list.isEmpty()) {
|
|
|
|
llAttachment.visibility = View.GONE
|
|
|
|
} else {
|
|
|
|
llAttachment.visibility = View.VISIBLE
|
|
|
|
var i = 0
|
|
|
|
val ie = ivMedia.size
|
|
|
|
while(i < ie) {
|
|
|
|
showAttachment_sub(ivMedia[i], i)
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showAttachment_sub(iv : MyNetworkImageView, idx : Int) {
|
|
|
|
if(idx >= attachment_list.size) {
|
|
|
|
iv.visibility = View.GONE
|
|
|
|
} else {
|
|
|
|
iv.visibility = View.VISIBLE
|
|
|
|
val pa = attachment_list[idx]
|
|
|
|
val a = pa.attachment
|
|
|
|
if(pa.status == PostAttachment.STATUS_UPLOADED && a != null) {
|
2018-01-18 08:53:32 +01:00
|
|
|
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), a.preview_url)
|
2018-01-04 19:52:25 +01:00
|
|
|
} else {
|
2018-01-18 08:53:32 +01:00
|
|
|
iv.setImageUrl(pref, Styler.calcIconRound(iv.layoutParams.width), null)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 添付した画像をタップ
|
|
|
|
private fun performAttachmentClick(idx : Int) {
|
|
|
|
val pa = attachment_list[idx]
|
|
|
|
|
|
|
|
AlertDialog.Builder(this)
|
|
|
|
.setTitle(R.string.media_attachment)
|
2018-01-21 13:46:36 +01:00
|
|
|
.setItems(
|
|
|
|
arrayOf<CharSequence>(
|
|
|
|
getString(R.string.set_description),
|
|
|
|
getString(R.string.delete)
|
|
|
|
)
|
|
|
|
) { _, i ->
|
2018-01-04 19:52:25 +01:00
|
|
|
when(i) {
|
|
|
|
0 -> editAttachmentDescription(pa)
|
|
|
|
1 -> deleteAttachment(pa)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun deleteAttachment(pa : PostAttachment) {
|
|
|
|
AlertDialog.Builder(this)
|
|
|
|
.setTitle(R.string.confirm_delete_attachment)
|
|
|
|
.setPositiveButton(R.string.ok) { _, _ ->
|
|
|
|
try {
|
|
|
|
attachment_list.remove(pa)
|
|
|
|
} catch(ignored : Throwable) {
|
|
|
|
}
|
|
|
|
|
|
|
|
showMediaAttachment()
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun editAttachmentDescription(pa : PostAttachment) {
|
|
|
|
val a = pa.attachment
|
|
|
|
if(a == null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.attachment_description_cant_edit_while_uploading)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
DlgTextInput.show(
|
|
|
|
this,
|
|
|
|
getString(R.string.attachment_description),
|
|
|
|
a.description,
|
|
|
|
object : DlgTextInput.Callback {
|
|
|
|
override fun onOK(dialog : Dialog, text : String) {
|
|
|
|
setAttachmentDescription(pa, dialog, text)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onEmptyError() {
|
|
|
|
showToast(this@ActPost, true, R.string.description_empty)
|
|
|
|
}
|
|
|
|
})
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressLint("StaticFieldLeak")
|
|
|
|
private fun setAttachmentDescription(pa : PostAttachment, dialog : Dialog, text : String) {
|
|
|
|
|
|
|
|
val attachment_id = pa.attachment?.id ?: return
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
TootTaskRunner(this).run(this@ActPost.account ?: return, object : TootTask {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
internal var new_attachment : TootAttachment? = null
|
|
|
|
|
|
|
|
override fun background(client : TootApiClient) : TootApiResult? {
|
|
|
|
val json = JSONObject()
|
|
|
|
try {
|
|
|
|
json.put("description", text)
|
|
|
|
} catch(ex : JSONException) {
|
|
|
|
log.trace(ex)
|
|
|
|
log.e(ex, "description encoding failed.")
|
|
|
|
}
|
|
|
|
|
|
|
|
val body_string = json.toString()
|
|
|
|
val request_body = RequestBody.create(
|
|
|
|
TootApiClient.MEDIA_TYPE_JSON, body_string
|
|
|
|
)
|
|
|
|
val request_builder = Request.Builder().put(request_body)
|
|
|
|
val result = client.request("/api/v1/media/" + attachment_id, request_builder)
|
2018-01-10 16:47:35 +01:00
|
|
|
new_attachment = parseItem(::TootAttachment, result?.jsonObject)
|
2018-01-04 19:52:25 +01:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun handleResult(result : TootApiResult?) {
|
|
|
|
if(result == null) return // cancelled.
|
|
|
|
|
|
|
|
val new_attachment = this.new_attachment
|
|
|
|
if(new_attachment != null) {
|
|
|
|
pa.attachment = new_attachment
|
|
|
|
showMediaAttachment()
|
|
|
|
|
|
|
|
try {
|
|
|
|
dialog.dismiss()
|
|
|
|
} catch(ignored : Throwable) {
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActPost, true, result.error)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun openAttachment() {
|
2018-01-21 13:46:36 +01:00
|
|
|
val permissionCheck =
|
|
|
|
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
preparePermission()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// permissionCheck = ContextCompat.checkSelfPermission( this, Manifest.permission.CAMERA );
|
|
|
|
// if( permissionCheck != PackageManager.PERMISSION_GRANTED ){
|
|
|
|
// preparePermission();
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
val a = ActionsDialog()
|
|
|
|
a.addAction(getString(R.string.image_pick)) { performAttachment() }
|
|
|
|
a.addAction(getString(R.string.image_capture)) { performCamera() }
|
|
|
|
|
|
|
|
// a.addAction( getString( R.string.video_capture ), new Runnable() {
|
|
|
|
// @Override public void run(){
|
|
|
|
// performCameraVideo();
|
|
|
|
// }
|
|
|
|
// } );
|
|
|
|
a.show(this, null)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun performAttachment() {
|
|
|
|
|
|
|
|
if(attachment_list.size >= 4) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.attachment_too_many)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(account == null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.account_select_please)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// SAFのIntentで開く
|
|
|
|
try {
|
|
|
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
|
|
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
|
|
|
intent.type = "*/*"
|
|
|
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
|
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
|
|
|
|
startActivityForResult(intent, REQUEST_CODE_ATTACHMENT)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
internal interface InputStreamOpener {
|
|
|
|
|
|
|
|
val mimeType : String
|
|
|
|
@Throws(IOException::class)
|
|
|
|
fun open() : InputStream
|
|
|
|
|
|
|
|
fun deleteTempFile()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun createOpener(uri : Uri, mime_type : String) : InputStreamOpener {
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
// 画像の種別
|
|
|
|
val is_jpeg = MIME_TYPE_JPEG == mime_type
|
|
|
|
val is_png = MIME_TYPE_PNG == mime_type
|
|
|
|
if(! is_jpeg && ! is_png) {
|
|
|
|
log.d("createOpener: source is not jpeg or png")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// 設定からリサイズ指定を読む
|
2018-01-17 02:16:26 +01:00
|
|
|
val resize_to = list_resize_max[Pref.ipResizeImage(pref)]
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
val bitmap = createResizedBitmap(
|
|
|
|
this,
|
|
|
|
uri,
|
|
|
|
resize_to,
|
|
|
|
skipIfNoNeedToResizeAndRotate = true
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(bitmap != null) {
|
|
|
|
try {
|
|
|
|
val cache_dir = externalCacheDir
|
|
|
|
if(cache_dir == null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, "getExternalCacheDir returns null.")
|
2018-01-04 19:52:25 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cache_dir.mkdir()
|
|
|
|
|
|
|
|
val temp_file = File(cache_dir, "tmp." + Thread.currentThread().id)
|
|
|
|
FileOutputStream(temp_file).use { os ->
|
|
|
|
if(is_jpeg) {
|
|
|
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 95, os)
|
|
|
|
} else {
|
|
|
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return object : ActPost.InputStreamOpener {
|
|
|
|
|
|
|
|
override val mimeType : String
|
|
|
|
get() = mime_type
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
override fun open() : InputStream {
|
|
|
|
return FileInputStream(temp_file)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun deleteTempFile() {
|
|
|
|
temp_file.delete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
bitmap.recycle()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "Resizing image failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return object : InputStreamOpener {
|
|
|
|
|
|
|
|
override val mimeType : String
|
|
|
|
get() = mime_type
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
override fun open() : InputStream {
|
|
|
|
return contentResolver.openInputStream(uri)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun deleteTempFile() {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
|
|
|
private fun addAttachment(uri : Uri, mime_type : String?) {
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
if(attachment_list.size >= 4) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.attachment_too_many)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
val account = this@ActPost.account
|
2018-01-04 19:52:25 +01:00
|
|
|
if(account == null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.account_select_please)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-20 17:59:38 +01:00
|
|
|
if(mime_type?.isEmpty() != false) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.mime_type_missing)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
2018-01-10 16:47:35 +01:00
|
|
|
} else if(! acceptable_mime_types.contains(mime_type)) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.mime_type_not_acceptable, mime_type)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
app_state.attachment_list = this.attachment_list
|
|
|
|
|
|
|
|
val pa = PostAttachment(this)
|
|
|
|
attachment_list.add(pa)
|
|
|
|
showMediaAttachment()
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.attachment_uploading)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
TootTaskRunner(this, TootTaskRunner.PROGRESS_NONE).run(account, object : TootTask {
|
2018-01-04 19:52:25 +01:00
|
|
|
override fun background(client : TootApiClient) : TootApiResult? {
|
2018-01-10 16:47:35 +01:00
|
|
|
if(mime_type.isEmpty()) {
|
|
|
|
return TootApiResult("mime_type is empty.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
val opener = createOpener(uri, mime_type)
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
val media_size_max =
|
|
|
|
1000000 * Math.max(1, Pref.spMediaSizeMax.optInt(pref) ?: 8)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val content_length = getStreamSize(true, opener.open())
|
|
|
|
if(content_length > media_size_max) {
|
2018-01-21 13:46:36 +01:00
|
|
|
return TootApiResult(
|
|
|
|
getString(
|
|
|
|
R.string.file_size_too_big,
|
|
|
|
media_size_max / 1000000
|
|
|
|
)
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
val multipart_body = MultipartBody.Builder()
|
|
|
|
.setType(MultipartBody.FORM)
|
|
|
|
.addFormDataPart(
|
|
|
|
"file", getDocumentName(uri), object : RequestBody() {
|
2018-01-21 13:46:36 +01:00
|
|
|
override fun contentType() : MediaType? {
|
|
|
|
return MediaType.parse(opener.mimeType)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
override fun contentLength() : Long {
|
|
|
|
return content_length
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
override fun writeTo(sink : BufferedSink) {
|
|
|
|
opener.open().use { inData ->
|
|
|
|
val tmp = ByteArray(4096)
|
|
|
|
while(true) {
|
|
|
|
val r = inData.read(tmp, 0, tmp.size)
|
|
|
|
if(r <= 0) break
|
|
|
|
sink.write(tmp, 0, r)
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
.build()
|
|
|
|
|
|
|
|
val request_builder = Request.Builder()
|
|
|
|
.post(multipart_body)
|
|
|
|
|
|
|
|
val result = client.request("/api/v1/media", request_builder)
|
|
|
|
|
|
|
|
opener.deleteTempFile()
|
2018-01-10 16:47:35 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
if(jsonObject != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val a = parseItem(::TootAttachment, jsonObject)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(a == null) {
|
|
|
|
result.error = "TootAttachment.parse failed"
|
2018-01-10 16:47:35 +01:00
|
|
|
} else {
|
2018-01-04 19:52:25 +01:00
|
|
|
pa.attachment = a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
return TootApiResult(ex.withCaption("read failed."))
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun handleResult(result : TootApiResult?) {
|
|
|
|
if(pa.attachment == null) {
|
|
|
|
pa.status = PostAttachment.STATUS_UPLOAD_FAILED
|
|
|
|
if(result != null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActPost, true, result.error)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pa.status = PostAttachment.STATUS_UPLOADED
|
|
|
|
}
|
|
|
|
// 投稿中に画面回転があった場合、新しい画面のコールバックを呼び出す必要がある
|
|
|
|
pa.callback?.onPostAttachmentComplete(pa)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// 添付メディア投稿が完了したら呼ばれる
|
|
|
|
override fun onPostAttachmentComplete(pa : PostAttachment) {
|
|
|
|
if(! attachment_list.contains(pa)) {
|
|
|
|
// この添付メディアはリストにない
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
when(pa.status) {
|
|
|
|
PostAttachment.STATUS_UPLOAD_FAILED -> {
|
|
|
|
// アップロード失敗
|
|
|
|
attachment_list.remove(pa)
|
|
|
|
showMediaAttachment()
|
|
|
|
}
|
|
|
|
|
|
|
|
PostAttachment.STATUS_UPLOADED -> {
|
|
|
|
val a = pa.attachment
|
|
|
|
if(a != null) {
|
|
|
|
// アップロード完了
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActPost, false, R.string.attachment_uploaded)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
// 投稿欄の末尾に追記する
|
|
|
|
val selStart = etContent.selectionStart
|
|
|
|
val selEnd = etContent.selectionEnd
|
|
|
|
val e = etContent.editableText
|
|
|
|
val len = e.length
|
|
|
|
val last_char = if(len <= 0) ' ' else e[len - 1]
|
|
|
|
if(! CharacterGroup.isWhitespace(last_char.toInt())) {
|
|
|
|
e.append(" ").append(a.text_url)
|
|
|
|
} else {
|
|
|
|
e.append(a.text_url)
|
|
|
|
}
|
|
|
|
etContent.setSelection(selStart, selEnd)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
showMediaAttachment()
|
|
|
|
}
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
// アップロード中…?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun performCamera() {
|
|
|
|
|
|
|
|
try {
|
|
|
|
// カメラで撮影
|
|
|
|
val filename = System.currentTimeMillis().toString() + ".jpg"
|
|
|
|
val values = ContentValues()
|
|
|
|
values.put(MediaStore.Images.Media.TITLE, filename)
|
|
|
|
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
2018-01-21 13:46:36 +01:00
|
|
|
uriCameraImage =
|
|
|
|
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
|
|
|
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriCameraImage)
|
|
|
|
|
|
|
|
startActivityForResult(intent, REQUEST_CODE_CAMERA)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "opening camera app failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun preparePermission() {
|
|
|
|
if(Build.VERSION.SDK_INT >= 23) {
|
|
|
|
// No explanation needed, we can request the permission.
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
ActivityCompat.requestPermissions(
|
|
|
|
this,
|
|
|
|
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) // Manifest.permission.CAMERA,
|
|
|
|
,
|
|
|
|
PERMISSION_REQUEST_CODE
|
2018-01-04 19:52:25 +01:00
|
|
|
)
|
|
|
|
} else {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.missing_permission_to_access_media)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRequestPermissionsResult(
|
|
|
|
requestCode : Int, permissions : Array<String>, grantResults : IntArray
|
|
|
|
) {
|
|
|
|
when(requestCode) {
|
|
|
|
PERMISSION_REQUEST_CODE -> {
|
|
|
|
var bNotGranted = false
|
|
|
|
var i = 0
|
|
|
|
val ie = permissions.size
|
|
|
|
while(i < ie) {
|
|
|
|
if(grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
bNotGranted = true
|
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
if(bNotGranted) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.missing_permission_to_access_media)
|
2018-01-04 19:52:25 +01:00
|
|
|
} else {
|
|
|
|
openAttachment()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun getDocumentName(uri : Uri) : String {
|
|
|
|
val errorName = "no_name"
|
|
|
|
return contentResolver.query(uri, null, null, null, null, null)
|
|
|
|
?.use { cursor ->
|
|
|
|
return if(! cursor.moveToFirst()) {
|
|
|
|
errorName
|
|
|
|
} else {
|
|
|
|
val colIdx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
|
|
if(cursor.isNull(colIdx)) {
|
|
|
|
errorName
|
|
|
|
} else {
|
|
|
|
cursor.getString(colIdx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
?: errorName
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
internal fun getStreamSize(bClose : Boolean, inStream : InputStream) : Long {
|
|
|
|
try {
|
|
|
|
var size = 0L
|
|
|
|
while(true) {
|
|
|
|
val r = IOUtils.skip(inStream, 16384)
|
|
|
|
if(r <= 0) break
|
|
|
|
size += r
|
|
|
|
}
|
|
|
|
return size
|
|
|
|
} finally {
|
|
|
|
@Suppress("DEPRECATION")
|
|
|
|
if(bClose) IOUtils.closeQuietly(inStream)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showVisibility() {
|
2018-01-21 13:46:36 +01:00
|
|
|
btnVisibility.setImageResource(Styler.getVisibilityIcon(this, visibility))
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun performVisibility() {
|
|
|
|
val caption_list = arrayOf(
|
|
|
|
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_WEB_SETTING),
|
|
|
|
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PUBLIC),
|
|
|
|
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_UNLISTED),
|
|
|
|
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PRIVATE),
|
|
|
|
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_DIRECT)
|
|
|
|
)
|
|
|
|
|
|
|
|
AlertDialog.Builder(this)
|
|
|
|
.setTitle(R.string.choose_visibility)
|
|
|
|
.setItems(caption_list) { _, which ->
|
|
|
|
when(which) {
|
|
|
|
0 -> visibility = TootStatus.VISIBILITY_WEB_SETTING
|
|
|
|
1 -> visibility = TootStatus.VISIBILITY_PUBLIC
|
|
|
|
2 -> visibility = TootStatus.VISIBILITY_UNLISTED
|
|
|
|
3 -> visibility = TootStatus.VISIBILITY_PRIVATE
|
|
|
|
4 -> visibility = TootStatus.VISIBILITY_DIRECT
|
|
|
|
}
|
|
|
|
showVisibility()
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
private fun performMore() {
|
|
|
|
val dialog = ActionsDialog()
|
|
|
|
|
2018-01-14 10:14:39 +01:00
|
|
|
dialog.addAction(
|
|
|
|
getString(R.string.open_picker_emoji)
|
|
|
|
) {
|
|
|
|
post_helper.openEmojiPickerFromMore()
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
dialog.addAction(
|
|
|
|
getString(R.string.clear_text)
|
|
|
|
) {
|
|
|
|
etContent.setText("")
|
|
|
|
etContentWarning.setText("")
|
|
|
|
}
|
2018-01-14 10:14:39 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
dialog.addAction(
|
|
|
|
getString(R.string.clear_text_and_media)
|
|
|
|
) {
|
|
|
|
etContent.setText("")
|
|
|
|
etContentWarning.setText("")
|
|
|
|
attachment_list.clear()
|
|
|
|
showMediaAttachment()
|
|
|
|
}
|
|
|
|
|
2018-01-14 10:14:39 +01:00
|
|
|
if(PostDraft.hasDraft()) {
|
|
|
|
dialog.addAction(
|
|
|
|
getString(R.string.restore_draft)
|
|
|
|
) { openDraftPicker() }
|
|
|
|
}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
dialog.addAction(
|
|
|
|
getString(R.string.recommended_plugin)
|
|
|
|
) { showRecommendedPlugin(null) }
|
2018-01-14 10:14:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
dialog.show(this, null)
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// post
|
|
|
|
|
|
|
|
private fun performPost() {
|
|
|
|
val account = this.account ?: return
|
|
|
|
|
|
|
|
// アップロード中は投稿できない
|
|
|
|
for(pa in attachment_list) {
|
|
|
|
if(pa.status == PostAttachment.STATUS_UPLOADING) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.media_attachment_still_uploading)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
post_helper.content = etContent.text.toString().trim { it <= ' ' }
|
|
|
|
|
|
|
|
if(! cbEnquete.isChecked) {
|
|
|
|
post_helper.enquete_items = null
|
|
|
|
} else {
|
|
|
|
val enquete_items = ArrayList<String>()
|
|
|
|
for(et in list_etChoice) {
|
|
|
|
enquete_items.add(et.text.toString().trim { it <= ' ' })
|
|
|
|
}
|
|
|
|
post_helper.enquete_items = enquete_items
|
|
|
|
}
|
|
|
|
|
|
|
|
if(! cbContentWarning.isChecked) {
|
2018-01-10 16:47:35 +01:00
|
|
|
post_helper.spoiler_text = null // nullはCWチェックなしを示す
|
2018-01-04 19:52:25 +01:00
|
|
|
} else {
|
|
|
|
post_helper.spoiler_text = etContentWarning.text.toString().trim { it <= ' ' }
|
|
|
|
}
|
|
|
|
|
|
|
|
post_helper.visibility = this.visibility
|
|
|
|
post_helper.bNSFW = cbNSFW.isChecked
|
|
|
|
|
|
|
|
post_helper.in_reply_to_id = this.in_reply_to_id
|
|
|
|
|
|
|
|
post_helper.attachment_list = this.attachment_list
|
|
|
|
|
|
|
|
post_helper.post(account, false, false) { target_account, status ->
|
|
|
|
val data = Intent()
|
|
|
|
data.putExtra(EXTRA_POSTED_ACCT, target_account.acct)
|
|
|
|
data.putExtra(EXTRA_POSTED_STATUS_ID, status.id)
|
|
|
|
|
|
|
|
setResult(RESULT_OK, data)
|
|
|
|
isPostComplete = true
|
|
|
|
this@ActPost.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun showReplyTo() {
|
|
|
|
if(in_reply_to_id == - 1L) {
|
|
|
|
llReply.visibility = View.GONE
|
|
|
|
} else {
|
|
|
|
llReply.visibility = View.VISIBLE
|
2018-01-11 10:31:25 +01:00
|
|
|
tvReplyTo.text = DecodeOptions(
|
|
|
|
short = true,
|
|
|
|
decodeEmoji = true
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-11 10:31:25 +01:00
|
|
|
).decodeHTML(this@ActPost, account, in_reply_to_text)
|
2018-01-18 08:53:32 +01:00
|
|
|
ivReply.setImageUrl(pref, Styler.calcIconRound(ivReply.layoutParams), in_reply_to_image)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun removeReply() {
|
|
|
|
in_reply_to_id = - 1L
|
|
|
|
in_reply_to_text = null
|
|
|
|
in_reply_to_image = null
|
|
|
|
in_reply_to_url = null
|
|
|
|
showReplyTo()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun saveDraft() {
|
|
|
|
val content = etContent.text.toString()
|
2018-01-21 13:46:36 +01:00
|
|
|
val content_warning =
|
|
|
|
if(cbContentWarning.isChecked) etContentWarning.text.toString() else ""
|
2018-01-04 19:52:25 +01:00
|
|
|
val isEnquete = cbEnquete.isChecked
|
|
|
|
|
|
|
|
val str_choice = arrayOf(
|
|
|
|
if(isEnquete) list_etChoice[0].text.toString() else "",
|
|
|
|
if(isEnquete) list_etChoice[1].text.toString() else "",
|
|
|
|
if(isEnquete) list_etChoice[2].text.toString() else "",
|
|
|
|
if(isEnquete) list_etChoice[3].text.toString() else ""
|
|
|
|
)
|
|
|
|
|
|
|
|
var hasContent = false
|
2018-01-10 16:47:35 +01:00
|
|
|
if(content.isNotBlank()) hasContent = true
|
|
|
|
if(content_warning.isNotBlank()) hasContent = true
|
2018-01-04 19:52:25 +01:00
|
|
|
for(s in str_choice) {
|
2018-01-10 16:47:35 +01:00
|
|
|
if(s.isNotBlank()) hasContent = true
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
if(! hasContent) {
|
|
|
|
log.d("saveDraft: dont save empty content")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
val tmp_attachment_list = JSONArray()
|
|
|
|
for(pa in attachment_list) {
|
|
|
|
val a = pa.attachment
|
|
|
|
if(a != null) tmp_attachment_list.put(a.json)
|
|
|
|
}
|
|
|
|
|
|
|
|
val json = JSONObject()
|
|
|
|
json.put(DRAFT_CONTENT, content)
|
|
|
|
json.put(DRAFT_CONTENT_WARNING, content_warning)
|
|
|
|
json.put(DRAFT_CONTENT_WARNING_CHECK, cbContentWarning.isChecked)
|
|
|
|
json.put(DRAFT_NSFW_CHECK, cbNSFW.isChecked)
|
|
|
|
json.put(DRAFT_VISIBILITY, visibility)
|
2018-01-21 13:46:36 +01:00
|
|
|
json.put(DRAFT_ACCOUNT_DB_ID, account?.db_id ?: - 1L)
|
2018-01-04 19:52:25 +01:00
|
|
|
json.put(DRAFT_ATTACHMENT_LIST, tmp_attachment_list)
|
|
|
|
json.put(DRAFT_REPLY_ID, in_reply_to_id)
|
|
|
|
json.put(DRAFT_REPLY_TEXT, in_reply_to_text)
|
|
|
|
json.put(DRAFT_REPLY_IMAGE, in_reply_to_image)
|
|
|
|
json.put(DRAFT_REPLY_URL, in_reply_to_url)
|
|
|
|
|
|
|
|
json.put(DRAFT_IS_ENQUETE, isEnquete)
|
|
|
|
val array = JSONArray()
|
|
|
|
for(s in str_choice) {
|
|
|
|
array.put(s)
|
|
|
|
}
|
|
|
|
json.put(DRAFT_ENQUETE_ITEMS, array)
|
|
|
|
|
|
|
|
PostDraft.save(System.currentTimeMillis(), json)
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun openDraftPicker() {
|
|
|
|
|
|
|
|
DlgDraftPicker().open(this) { draft -> restoreDraft(draft) }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun restoreDraft(draft : JSONObject) {
|
|
|
|
|
|
|
|
@Suppress("DEPRECATION")
|
2018-01-13 07:38:32 +01:00
|
|
|
val progress = ProgressDialogEx(this)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val task = @SuppressLint("StaticFieldLeak")
|
2018-01-10 16:47:35 +01:00
|
|
|
object : AsyncTask<Void, String, String?>() {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
internal val list_warning = ArrayList<String>()
|
|
|
|
internal var account : SavedAccount? = null
|
|
|
|
|
|
|
|
override fun doInBackground(vararg params : Void) : String? {
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
var content = draft.parseString(DRAFT_CONTENT) ?: ""
|
|
|
|
val account_db_id = draft.parseLong(DRAFT_ACCOUNT_DB_ID) ?: - 1L
|
2018-01-04 19:52:25 +01:00
|
|
|
var tmp_attachment_list = draft.optJSONArray(DRAFT_ATTACHMENT_LIST)
|
|
|
|
|
|
|
|
val account = SavedAccount.loadAccount(this@ActPost, account_db_id)
|
|
|
|
if(account == null) {
|
|
|
|
list_warning.add(getString(R.string.account_in_draft_is_lost))
|
|
|
|
try {
|
|
|
|
var i = 0
|
|
|
|
val ie = tmp_attachment_list.length()
|
|
|
|
while(i < ie) {
|
2018-01-21 13:46:36 +01:00
|
|
|
val ta =
|
|
|
|
parseItem(::TootAttachment, tmp_attachment_list.optJSONObject(i))
|
2018-01-10 16:47:35 +01:00
|
|
|
val text_url = ta?.text_url
|
|
|
|
if(text_url?.isNotEmpty() == true) {
|
|
|
|
content = content.replace(text_url, "")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
tmp_attachment_list = JSONArray()
|
|
|
|
draft.put(DRAFT_ATTACHMENT_LIST, tmp_attachment_list)
|
|
|
|
draft.put(DRAFT_CONTENT, content)
|
|
|
|
draft.remove(DRAFT_REPLY_ID)
|
|
|
|
draft.remove(DRAFT_REPLY_TEXT)
|
|
|
|
draft.remove(DRAFT_REPLY_IMAGE)
|
|
|
|
draft.remove(DRAFT_REPLY_URL)
|
|
|
|
} catch(ignored : JSONException) {
|
|
|
|
}
|
|
|
|
|
|
|
|
return "OK"
|
|
|
|
}
|
|
|
|
this.account = account
|
|
|
|
|
|
|
|
// アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ
|
2018-01-21 13:46:36 +01:00
|
|
|
val api_client = TootApiClient(this@ActPost, callback = object : TootApiCallback {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
override val isApiCancelled : Boolean
|
2018-01-10 16:47:35 +01:00
|
|
|
get() = isCancelled
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
override fun publishApiProgress(s : String) {
|
2018-01-21 13:46:36 +01:00
|
|
|
runOnMainLooper { progress.setMessage(s) }
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2018-01-12 10:01:25 +01:00
|
|
|
api_client.account = account
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
if(in_reply_to_id != - 1L) {
|
|
|
|
val result = api_client.request("/api/v1/statuses/" + in_reply_to_id)
|
|
|
|
if(isCancelled) return null
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
if(jsonObject == null) {
|
|
|
|
list_warning.add(getString(R.string.reply_to_in_draft_is_lost))
|
|
|
|
draft.remove(DRAFT_REPLY_ID)
|
|
|
|
draft.remove(DRAFT_REPLY_TEXT)
|
|
|
|
draft.remove(DRAFT_REPLY_IMAGE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
var isSomeAttachmentRemoved = false
|
|
|
|
for(i in tmp_attachment_list.length() - 1 downTo 0) {
|
|
|
|
if(isCancelled) return null
|
2018-01-10 16:47:35 +01:00
|
|
|
val ta = parseItem(::TootAttachment, tmp_attachment_list.optJSONObject(i))
|
2018-01-04 19:52:25 +01:00
|
|
|
if(ta == null) {
|
|
|
|
isSomeAttachmentRemoved = true
|
|
|
|
tmp_attachment_list.remove(i)
|
|
|
|
} else if(! check_exist(ta.url)) {
|
|
|
|
isSomeAttachmentRemoved = true
|
|
|
|
tmp_attachment_list.remove(i)
|
2018-01-10 16:47:35 +01:00
|
|
|
val text_url = ta.text_url
|
|
|
|
if(text_url?.isNotEmpty() == true) {
|
|
|
|
content = content.replace(text_url, "")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(isSomeAttachmentRemoved) {
|
|
|
|
list_warning.add(getString(R.string.attachment_in_draft_is_lost))
|
|
|
|
draft.put(DRAFT_ATTACHMENT_LIST, tmp_attachment_list)
|
|
|
|
draft.put(DRAFT_CONTENT, content)
|
|
|
|
}
|
|
|
|
} catch(ex : JSONException) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
return "OK"
|
|
|
|
}
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
override fun onCancelled(result : String?) {
|
2018-01-04 19:52:25 +01:00
|
|
|
onPostExecute(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPostExecute(result : String?) {
|
|
|
|
try {
|
|
|
|
progress.dismiss()
|
|
|
|
} catch(ignored : Throwable) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(isCancelled || result == null) {
|
|
|
|
// cancelled.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
val content = draft.optString(DRAFT_CONTENT)
|
|
|
|
val content_warning = draft.optString(DRAFT_CONTENT_WARNING)
|
|
|
|
val content_warning_checked = draft.optBoolean(DRAFT_CONTENT_WARNING_CHECK)
|
|
|
|
val nsfw_checked = draft.optBoolean(DRAFT_NSFW_CHECK)
|
|
|
|
val tmp_attachment_list = draft.optJSONArray(DRAFT_ATTACHMENT_LIST)
|
2018-01-21 13:46:36 +01:00
|
|
|
val reply_id = draft.parseLong(DRAFT_REPLY_ID) ?: - 1L
|
2018-01-04 19:52:25 +01:00
|
|
|
val reply_text = draft.optString(DRAFT_REPLY_TEXT, null)
|
|
|
|
val reply_image = draft.optString(DRAFT_REPLY_IMAGE, null)
|
|
|
|
val reply_url = draft.optString(DRAFT_REPLY_URL, null)
|
2018-01-21 13:46:36 +01:00
|
|
|
val draft_visibility = draft.parseString(DRAFT_VISIBILITY)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
val evEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this@ActPost, content)
|
2018-01-16 07:48:17 +01:00
|
|
|
etContent.setText(evEmoji)
|
|
|
|
etContent.setSelection(evEmoji.length)
|
2018-01-04 19:52:25 +01:00
|
|
|
etContentWarning.setText(content_warning)
|
|
|
|
etContentWarning.setSelection(content_warning.length)
|
|
|
|
cbContentWarning.isChecked = content_warning_checked
|
|
|
|
cbNSFW.isChecked = nsfw_checked
|
2018-01-21 13:46:36 +01:00
|
|
|
if(draft_visibility != null) this@ActPost.visibility = draft_visibility
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
cbEnquete.isChecked = draft.optBoolean(DRAFT_IS_ENQUETE, false)
|
|
|
|
val array = draft.optJSONArray(DRAFT_ENQUETE_ITEMS)
|
|
|
|
if(array != null) {
|
|
|
|
var src_index = 0
|
|
|
|
for(et in list_etChoice) {
|
|
|
|
if(src_index < array.length()) {
|
|
|
|
et.setText(array.optString(src_index))
|
|
|
|
++ src_index
|
|
|
|
} else {
|
|
|
|
et.setText("")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(account != null) selectAccount(account)
|
|
|
|
|
|
|
|
if(tmp_attachment_list.length() > 0) {
|
|
|
|
attachment_list.clear()
|
|
|
|
var i = 0
|
|
|
|
val ie = tmp_attachment_list.length()
|
|
|
|
while(i < ie) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val ta = parseItem(::TootAttachment, tmp_attachment_list.optJSONObject(i))
|
2018-01-04 19:52:25 +01:00
|
|
|
if(ta != null) {
|
|
|
|
val pa = PostAttachment(ta)
|
|
|
|
attachment_list.add(pa)
|
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(reply_id != - 1L) {
|
|
|
|
in_reply_to_id = reply_id
|
|
|
|
in_reply_to_text = reply_text
|
|
|
|
in_reply_to_image = reply_image
|
|
|
|
in_reply_to_url = reply_url
|
|
|
|
}
|
|
|
|
|
|
|
|
updateContentWarning()
|
|
|
|
showMediaAttachment()
|
|
|
|
showVisibility()
|
|
|
|
updateTextCount()
|
|
|
|
showReplyTo()
|
|
|
|
showEnquete()
|
|
|
|
|
|
|
|
if(! list_warning.isEmpty()) {
|
|
|
|
val sb = StringBuilder()
|
|
|
|
for(s in list_warning) {
|
|
|
|
if(sb.isNotEmpty()) sb.append("\n")
|
|
|
|
sb.append(s)
|
|
|
|
}
|
|
|
|
AlertDialog.Builder(this@ActPost)
|
|
|
|
.setMessage(sb)
|
|
|
|
.setNeutralButton(R.string.close, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
progress.isIndeterminate = true
|
|
|
|
progress.setCancelable(true)
|
|
|
|
progress.setOnCancelListener { task.cancel(true) }
|
|
|
|
progress.show()
|
|
|
|
task.executeOnExecutor(App1.task_executor)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun prepareMushroomText(et : EditText) : String {
|
|
|
|
mushroom_start = et.selectionStart
|
|
|
|
mushroom_end = et.selectionEnd
|
|
|
|
return if(mushroom_end > mushroom_start) {
|
|
|
|
et.text.toString().substring(mushroom_start, mushroom_end)
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun applyMushroomText(et : EditText, text : String) {
|
|
|
|
val src = et.text.toString()
|
|
|
|
if(mushroom_start > src.length) mushroom_start = src.length
|
|
|
|
if(mushroom_end > src.length) mushroom_end = src.length
|
|
|
|
|
|
|
|
val sb = StringBuilder()
|
|
|
|
sb.append(src.substring(0, mushroom_start))
|
|
|
|
// int new_sel_start = sb.length();
|
|
|
|
sb.append(text)
|
|
|
|
val new_sel_end = sb.length
|
|
|
|
sb.append(src.substring(mushroom_end))
|
|
|
|
et.setText(sb)
|
|
|
|
et.setSelection(new_sel_end, new_sel_end)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun openMushroom() {
|
|
|
|
try {
|
|
|
|
var text : String? = null
|
|
|
|
when {
|
|
|
|
etContentWarning.hasFocus() -> {
|
|
|
|
mushroom_input = 1
|
|
|
|
text = prepareMushroomText(etContentWarning)
|
|
|
|
}
|
|
|
|
|
|
|
|
etContent.hasFocus() -> {
|
|
|
|
mushroom_input = 0
|
|
|
|
text = prepareMushroomText(etContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
else -> for(i in 0 .. 3) {
|
|
|
|
if(list_etChoice[i].hasFocus()) {
|
|
|
|
mushroom_input = i + 2
|
|
|
|
text = prepareMushroomText(list_etChoice[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(text == null) {
|
|
|
|
mushroom_input = 0
|
|
|
|
text = prepareMushroomText(etContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
val intent = Intent("com.adamrocker.android.simeji.ACTION_INTERCEPT")
|
|
|
|
intent.addCategory("com.adamrocker.android.simeji.REPLACE")
|
|
|
|
intent.putExtra("replace_key", text)
|
|
|
|
|
|
|
|
// Create intent to show chooser
|
|
|
|
val chooser = Intent.createChooser(intent, getString(R.string.select_plugin))
|
|
|
|
|
|
|
|
// Verify the intent will resolve to at least one activity
|
|
|
|
if(intent.resolveActivity(packageManager) == null) {
|
|
|
|
showRecommendedPlugin(getString(R.string.plugin_not_installed))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
startActivityForResult(chooser, REQUEST_CODE_MUSHROOM)
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
showRecommendedPlugin(getString(R.string.plugin_not_installed))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun applyMushroomResult(text : String) {
|
|
|
|
when(mushroom_input) {
|
|
|
|
0 -> applyMushroomText(etContent, text)
|
|
|
|
1 -> applyMushroomText(etContentWarning, text)
|
|
|
|
else -> for(i in 0 .. 3) {
|
|
|
|
if(mushroom_input == i + 2) {
|
|
|
|
applyMushroomText(list_etChoice[i], text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressLint("InflateParams")
|
|
|
|
private fun showRecommendedPlugin(title : String?) {
|
|
|
|
|
|
|
|
val res_id = when(getString(R.string.language_code)) {
|
|
|
|
"ja" -> R.raw.recommended_plugin_ja
|
|
|
|
"fr" -> R.raw.recommended_plugin_fr
|
|
|
|
else -> R.raw.recommended_plugin_en
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
this.loadRawResource(res_id)?.let { data ->
|
|
|
|
val text = data.decodeUTF8()
|
2018-01-04 19:52:25 +01:00
|
|
|
val viewRoot = layoutInflater.inflate(R.layout.dlg_plugin_missing, null, false)
|
|
|
|
|
|
|
|
val tvText = viewRoot.findViewById<TextView>(R.id.tvText)
|
|
|
|
val lcc = object : LinkClickContext {}
|
|
|
|
val sv = DecodeOptions().decodeHTML(this@ActPost, lcc, text)
|
|
|
|
tvText.text = sv
|
|
|
|
tvText.movementMethod = LinkMovementMethod.getInstance()
|
|
|
|
|
|
|
|
val tvTitle = viewRoot.findViewById<TextView>(R.id.tvTitle)
|
2018-01-10 16:47:35 +01:00
|
|
|
if(title?.isEmpty() != false) {
|
2018-01-04 19:52:25 +01:00
|
|
|
tvTitle.visibility = View.GONE
|
|
|
|
} else {
|
|
|
|
tvTitle.text = title
|
|
|
|
}
|
|
|
|
|
|
|
|
AlertDialog.Builder(this)
|
|
|
|
.setView(viewRoot)
|
|
|
|
.setCancelable(true)
|
|
|
|
.setNeutralButton(R.string.close, null)
|
|
|
|
.show()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showEnquete() {
|
|
|
|
llEnquete.visibility = if(cbEnquete.isChecked) View.VISIBLE else View.GONE
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|