diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt index b0b29eab..5832af27 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt @@ -40,6 +40,7 @@ import okhttp3.RequestBody import okio.BufferedSink import org.json.JSONObject import java.io.* +import java.util.regex.Pattern class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundButton.OnCheckedChangeListener { @@ -1157,7 +1158,8 @@ class ActAccountSetting private fun sendNote(bConfirmed : Boolean = false) { val sv = etNote.text.toString() if(! bConfirmed) { - val length = sv.codePointCount(0, sv.length) + + val length = countNoteText(sv) if(length > max_length_note) { AlertDialog.Builder(this) .setMessage( @@ -1178,6 +1180,18 @@ class ActAccountSetting updateCredential("note", EmojiDecoder.decodeShortCode(sv)) } + // Mastodon 2.7 でnoteの文字数計算が変わる + // https://github.com/tootsuite/mastodon/commit/45899cfa691b1e4f43da98c456ae8faa584eb437 + private val reLinkUrl = Pattern.compile("""(https?://[\w/:%#@${'$'}&?!()\[\]~.=+\-]+)""") + private val reMention =Pattern.compile("""(?<=^|[^/\w\p{Pc}])@((\w+([\w.-]+\w+)?)(?:@[a-z0-9.\-]+[a-z0-9]+)?)""",Pattern.CASE_INSENSITIVE) + private val strUrlReplacement = (0 until 23).map{ ' '}.joinToString() + private fun countNoteText(s:String):Int{ + val s2 = s + .replaceAll(reLinkUrl,strUrlReplacement) + .replaceAll(reMention,"@\\2") + return s2.codePointCount(0,s2.length) + } + private fun sendLocked(willLocked : Boolean) { updateCredential("locked", willLocked) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt index 45134c77..8d0a5c83 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt @@ -725,16 +725,22 @@ class ActMain : AppCompatActivity() etQuickToot.hideKeyboard() - post_helper.post( - account - ) { target_account, status -> - etQuickToot.setText("") - posted_acct = target_account.acct - posted_status_id = status.id - posted_reply_id = status.in_reply_to_id - posted_redraft_id = null - refreshAfterPost() - } + post_helper.post(account,callback=object:PostHelper.PostCompleteCallback{ + override fun onPostComplete( + target_account : SavedAccount, + status : TootStatus + ) { + etQuickToot.setText("") + posted_acct = target_account.acct + posted_status_id = status.id + posted_reply_id = status.in_reply_to_id + posted_redraft_id = null + refreshAfterPost() + } + + override fun onScheduledPostComplete(target_account : SavedAccount) { // TODO + } + }) } override fun onPageScrolled( @@ -1102,6 +1108,13 @@ class ActMain : AppCompatActivity() , bAllowPseudo = false , bAllowMisskey = false ) + R.id.nav_scheduled_statuses_list-> Action_Account.timeline( + this + , defaultInsertPosition + , Column.TYPE_SCHEDULED_STATUS + , bAllowPseudo = false + , bAllowMisskey = false + ) R.id.nav_add_list -> Action_Account.timeline( this , defaultInsertPosition diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt index 2a59b2cf..ac23497a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt @@ -30,6 +30,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.* +import jp.juggler.subwaytooter.R.string.status import jp.juggler.subwaytooter.api.* import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.dialog.* @@ -57,7 +58,9 @@ import java.util.* import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicBoolean -class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callback { +class ActPost : AppCompatActivity(), + View.OnClickListener, + PostAttachment.Callback { companion object { internal val log = LogCategory("ActPost") @@ -166,6 +169,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba private const val STATE_MUSHROOM_END = "mushroom_end" private const val STATE_REDRAFT_STATUS_ID = "redraft_status_id" private const val STATE_URI_CAMERA_IMAGE = "uri_camera_image" + private const val STATE_TIME_SCHEDULE = "time_schedule" fun open( activity : Activity, @@ -256,6 +260,10 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba private lateinit var ivReply : MyNetworkImageView private lateinit var scrollView : ScrollView + private lateinit var tvSchedule : TextView + private lateinit var ibSchedule : ImageButton + private lateinit var ibScheduleReset : ImageButton + internal lateinit var pref : SharedPreferences internal lateinit var app_state : AppState private lateinit var post_helper : PostHelper @@ -268,6 +276,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba private var redraft_status_id : EntityId? = null + private var timeSchedule = 0L + private val text_watcher : TextWatcher = object : TextWatcher { override fun beforeTextChanged(charSequence : CharSequence, i : Int, i1 : Int, i2 : Int) { @@ -308,7 +318,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba private var mushroom_end : Int = 0 private val link_click_listener : MyClickableSpanClickCallback = { _, span -> - App1.openBrowser(this@ActPost,span.url) + App1.openBrowser(this@ActPost, span.url) } //////////////////////////////////////////////////////////////// @@ -327,6 +337,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba R.id.btnMore -> performMore() R.id.btnPlugin -> openMushroom() R.id.btnEmojiPicker -> post_helper.openEmojiPickerFromMore() + R.id.ibSchedule -> performSchedule() + R.id.ibScheduleReset -> resetSchedule() } } @@ -415,6 +427,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba mushroom_start = savedInstanceState.getInt(STATE_MUSHROOM_START, 0) mushroom_end = savedInstanceState.getInt(STATE_MUSHROOM_END, 0) redraft_status_id = EntityId.from(savedInstanceState, STATE_REDRAFT_STATUS_ID) + timeSchedule = savedInstanceState.getLong(STATE_TIME_SCHEDULE, 0L) savedInstanceState.getString(STATE_URI_CAMERA_IMAGE).mayUri()?.let { uriCameraImage = it @@ -724,6 +737,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba showReplyTo() showEnquete() showQuotedRenote() + showSchedule() } override fun onDestroy() { @@ -741,6 +755,9 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba outState.putInt(STATE_MUSHROOM_START, mushroom_start) outState.putInt(STATE_MUSHROOM_END, mushroom_end) redraft_status_id?.putTo(outState, STATE_REDRAFT_STATUS_ID) + + outState.putLong(STATE_TIME_SCHEDULE, timeSchedule) + if(uriCameraImage != null) { outState.putString(STATE_URI_CAMERA_IMAGE, uriCameraImage.toString()) } @@ -894,6 +911,13 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba btnRemoveReply = findViewById(R.id.btnRemoveReply) ivReply = findViewById(R.id.ivReply) + tvSchedule = findViewById(R.id.tvSchedule) + ibSchedule = findViewById(R.id.ibSchedule) + ibScheduleReset= findViewById(R.id.ibScheduleReset) + + ibSchedule.setOnClickListener(this) + ibScheduleReset.setOnClickListener(this) + account_list = SavedAccount.loadAccountList(this@ActPost) SavedAccount.sort(account_list) @@ -969,18 +993,19 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba private var lastInstanceTask : TootTaskRunner? = null private fun getMaxCharCount() : Int { - + val account = account - - when{ - account == null || account.isPseudo -> {} - - else->{ + + when { + account == null || account.isPseudo -> { + } + + else -> { val info = account.instance - + // 情報がないか古いなら再取得 if(info == null || System.currentTimeMillis() - info.time_parse >= 300000L) { - + // 同時に実行するタスクは1つまで var lastTask = lastInstanceTask if(lastTask?.isActive != true) { @@ -990,15 +1015,16 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba var newInfo : TootInstance? = null override fun background(client : TootApiClient) : TootApiResult? { - val result = if( account.isMisskey){ + val result = if(account.isMisskey) { client.request( "/api/meta", account.putMisskeyApiToken().toPostRequestBuilder() ) - }else{ + } else { client.request("/api/v1/instance") } - newInfo = TootParser(this@ActPost, account).instance(result?.jsonObject) + newInfo = + TootParser(this@ActPost, account).instance(result?.jsonObject) return result } @@ -1299,7 +1325,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba override fun background(client : TootApiClient) : TootApiResult? { try { val result = client.request( - "/api/v1/media/${attachment.id}" , + "/api/v1/media/${attachment.id}", JSONObject() .put("focus", "%.2f,%.2f".format(x, y)) .toPutRequestBuilder() @@ -1809,7 +1835,6 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba } } ) - val result = client.request( "/api/v1/media", @@ -2090,16 +2115,30 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba post_helper.useQuotedRenote = cbQuoteRenote.isChecked - post_helper.post(account) { target_account, status -> - val data = Intent() - data.putExtra(EXTRA_POSTED_ACCT, target_account.acct) - status.id.putTo(data, EXTRA_POSTED_STATUS_ID) - redraft_status_id?.putTo(data, EXTRA_POSTED_REDRAFT_ID) - status.in_reply_to_id?.putTo(data, EXTRA_POSTED_REPLY_ID) - setResult(RESULT_OK, data) - isPostComplete = true - this@ActPost.finish() - } + post_helper.scheduledAt = timeSchedule + + post_helper.post(account,callback=object:PostHelper.PostCompleteCallback{ + override fun onPostComplete( + target_account : SavedAccount, + status : TootStatus + ) { + val data = Intent() + data.putExtra(EXTRA_POSTED_ACCT, target_account.acct) + status.id.putTo(data, EXTRA_POSTED_STATUS_ID) + redraft_status_id?.putTo(data, EXTRA_POSTED_REDRAFT_ID) + status.in_reply_to_id?.putTo(data, EXTRA_POSTED_REPLY_ID) + setResult(RESULT_OK, data) + isPostComplete = true + this@ActPost.finish() + } + + override fun onScheduledPostComplete(target_account : SavedAccount) { + showToast(this@ActPost,false,getString(R.string.scheduled_status_sent)) + setResult(Activity.RESULT_CANCELED) + isPostComplete = true + this@ActPost.finish() + } + }) } private fun showQuotedRenote() { @@ -2549,4 +2588,23 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba true } + + private fun showSchedule() { + tvSchedule.text = when(timeSchedule) { + 0L -> getString(R.string.unspecified) + else -> TootStatus.formatTime(this, timeSchedule, Pref.bpRelativeTimestamp(pref)) + } + } + + private fun performSchedule() { + DlgDateTime(this).open(timeSchedule) { t -> + timeSchedule = t + showSchedule() + } + } + + private fun resetSchedule(){ + timeSchedule = 0L + showSchedule() + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.kt b/app/src/main/java/jp/juggler/subwaytooter/Column.kt index b4b332c1..bd3200a3 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.kt @@ -218,6 +218,7 @@ class Column( internal const val TYPE_LOCAL_AROUND = 29 internal const val TYPE_FEDERATED_AROUND = 30 internal const val TYPE_ACCOUNT_AROUND = 31 + internal const val TYPE_SCHEDULED_STATUS = 33 internal const val TAB_STATUS = 0 internal const val TAB_FOLLOWING = 1 @@ -279,6 +280,7 @@ class Column( TYPE_LIST_TL -> context.getString(R.string.list_timeline) TYPE_DIRECT_MESSAGES -> context.getString(R.string.direct_messages) TYPE_TREND_TAG -> context.getString(R.string.trend_tag) + TYPE_SCHEDULED_STATUS ->context.getString(R.string.scheduled_status) else -> "?" } } @@ -317,6 +319,7 @@ class Column( TYPE_LIST_TL -> R.attr.ic_list_tl TYPE_DIRECT_MESSAGES -> R.attr.ic_mail TYPE_TREND_TAG -> R.attr.ic_hashtag + TYPE_SCHEDULED_STATUS -> R.attr.ic_timer else -> R.attr.ic_info } } @@ -803,8 +806,8 @@ class Column( when(column_type) { - TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND -> status_id = - when(isMisskey) { + TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND -> + status_id = when(isMisskey) { true -> EntityId.mayNull(src.parseString(KEY_STATUS_ID)) else -> EntityId.mayNull(src.parseLong(KEY_STATUS_ID)) } @@ -2582,6 +2585,18 @@ class Column( } + private fun getScheduledStatuses(client:TootApiClient):TootApiResult?{ + val result = client.request("/api/v1/scheduled_statuses") + val src = parser.statusList(result?.jsonArray) + list_tmp = addWithFilterStatus(list_tmp,src) + + // TODO: paging? + idOld = null + idRecent = null + + return result + } + override fun doInBackground(vararg unused : Void) : TootApiResult? { ctStarted.set(true) @@ -3268,6 +3283,8 @@ class Column( return result } + TYPE_SCHEDULED_STATUS -> return getScheduledStatuses(client) + else -> return getStatuses(client, makeHomeTlUrl()) } } finally { @@ -6199,6 +6216,7 @@ class Column( TYPE_CONVERSATION, TYPE_LIST_LIST, TYPE_TREND_TAG, + TYPE_SCHEDULED_STATUS, TYPE_FOLLOW_SUGGESTION -> true TYPE_LIST_MEMBER, diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgDateTime.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgDateTime.kt new file mode 100644 index 00000000..4e943638 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgDateTime.kt @@ -0,0 +1,126 @@ +package jp.juggler.subwaytooter.dialog + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.Dialog +import android.os.Build +import android.provider.Settings +import android.view.View +import android.view.WindowManager +import android.widget.Button +import android.widget.DatePicker +import android.widget.TimePicker +import jp.juggler.subwaytooter.R +import java.util.* +import android.provider.Settings.System.TIME_12_24 + +class DlgDateTime( + val activity : Activity +) : DatePicker.OnDateChangedListener, View.OnClickListener { + + private lateinit var datePicker : DatePicker + private lateinit var timePicker : TimePicker + private lateinit var btnCancel : Button + private lateinit var btnOk : Button + private lateinit var dialog : Dialog + + private lateinit var callback : (Long) -> Unit + + @SuppressLint("InflateParams") + fun open(initialValue : Long, callback : (Long) -> Unit) { + this.callback = callback + + val view = activity.layoutInflater.inflate(R.layout.dlg_date_time, null, false) + + datePicker = view.findViewById(R.id.datePicker) + timePicker = view.findViewById(R.id.timePicker) + btnCancel = view.findViewById(R.id.btnCancel) + btnOk = view.findViewById(R.id.btnOk) + + val c = GregorianCalendar.getInstance(TimeZone.getDefault()) + c.timeInMillis = when(initialValue) { + 0L -> System.currentTimeMillis() + 10 * 60000L + else -> initialValue + } + datePicker.firstDayOfWeek = Calendar.MONDAY + datePicker.init( + c.get(Calendar.YEAR), + c.get(Calendar.MONTH), + c.get(Calendar.DAY_OF_MONTH), + this + ) + + if(Build.VERSION.SDK_INT >= 23) { + timePicker.hour = c.get(Calendar.HOUR_OF_DAY) + timePicker.minute = c.get(Calendar.MINUTE) + } else { + @Suppress("DEPRECATION") + timePicker.currentHour = c.get(Calendar.HOUR_OF_DAY) + @Suppress("DEPRECATION") + timePicker.currentMinute = c.get(Calendar.MINUTE) + } + + timePicker.setIs24HourView( + when(Settings.System.getString(activity.contentResolver, Settings.System.TIME_12_24)) { + "12" -> false + else -> true + } + ) + + btnCancel.setOnClickListener(this) + btnOk.setOnClickListener(this) + + dialog = Dialog(activity) + dialog.setContentView(view) + dialog.window?.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.WRAP_CONTENT + ) + dialog.show() + } + + override fun onClick(v : View) { + when(v.id) { + R.id.btnCancel -> dialog.cancel() + + R.id.btnOk -> { + dialog.dismiss() + callback(getTime()) + } + } + } + + override fun onDateChanged( + view : DatePicker, + year : Int, + monthOfYear : Int, + dayOfMonth : Int + ) { + // nothing to do + } + + private fun getTime() : Long { + val y = datePicker.year + val m = datePicker.month + val d = datePicker.dayOfMonth + + val h : Int + val j : Int + if(Build.VERSION.SDK_INT >= 23) { + h = timePicker.hour + j = timePicker.minute + } else { + @Suppress("DEPRECATION") + h = timePicker.currentHour + @Suppress("DEPRECATION") + j = timePicker.currentMinute + } + + val c = GregorianCalendar.getInstance(TimeZone.getDefault()) + c.set(y, m, d, h, j) + c.set(Calendar.SECOND, 0) + c.set(Calendar.MILLISECOND, 0) + return c.timeInMillis + } + +} \ No newline at end of file diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt index b50c9e72..f9bfc8f7 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/EmojiDecoder.kt @@ -202,7 +202,7 @@ object EmojiDecoder { private interface ShortCodeSplitterCallback { fun onString(part : String) // shortcode以外の文字列 - fun onShortCode(part : String, name : String) // part : ":shortcode:", name : "shortcode" + fun onShortCode(prevCodePoint:Int,part : String, name : String) // part : ":shortcode:", name : "shortcode" } private fun splitShortCode( @@ -256,9 +256,16 @@ object EmojiDecoder { continue } + val prevCodePoint = if(start >0){ + s.codePointBefore(start) + }else{ + 0x20 + } + callback.onShortCode( - s.substring(start, posEndColon + 1) // ":shortcode:" - , s.substring(start + 1, posEndColon) // "shortcode" + prevCodePoint, + s.substring(start, posEndColon + 1), // ":shortcode:" + s.substring(start + 1, posEndColon) // "shortcode" ) i = posEndColon + 1 // コロンの次の位置 @@ -280,7 +287,7 @@ object EmojiDecoder { builder.addUnicodeString(part) } - override fun onShortCode(part : String, name : String) { + override fun onShortCode(prevCodePoint:Int,part : String, name : String) { // フレニコのプロフ絵文字 if(emojiMapProfile != null && name.length >= 2 && name[0] == '@') { @@ -346,7 +353,7 @@ object EmojiDecoder { sb.append(part) } - override fun onShortCode(part : String, name : String) { + override fun onShortCode(prevCodePoint:Int,part : String, name : String) { // カスタム絵文字にマッチするなら変換しない val emojiCustom = emojiMapCustom?.get(name) diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/PostHelper.kt b/app/src/main/java/jp/juggler/subwaytooter/util/PostHelper.kt index a1dd33c8..2c6a1ced 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/PostHelper.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/PostHelper.kt @@ -56,19 +56,25 @@ class PostHelper( } + interface PostCompleteCallback { + fun onPostComplete(target_account : SavedAccount, status : TootStatus) + fun onScheduledPostComplete(target_account : SavedAccount) + } + /////////////////////////////////////////////////////////////////////////////////// // 投稿機能 var content : String? = null var spoiler_text : String? = null var visibility : TootVisibility = TootVisibility.Public - var bNSFW : Boolean = false + var bNSFW = false var in_reply_to_id : EntityId? = null var attachment_list : ArrayList? = null var enquete_items : ArrayList? = null var emojiMapCustom : HashMap? = null var redraft_status_id : EntityId? = null - var useQuotedRenote : Boolean = false + var useQuotedRenote = false + var scheduledAt = 0L private var last_post_tapped : Long = 0L @@ -88,6 +94,7 @@ class PostHelper( val attachment_list = this.attachment_list val enquete_items = this.enquete_items val visibility = this.visibility + val scheduledAt = this.scheduledAt val hasAttachment = attachment_list?.isNotEmpty() ?: false @@ -224,6 +231,8 @@ class PostHelper( val parser = TootParser(activity, account) + var scheduledStatusSucceeded = false + fun getInstanceInformation(client : TootApiClient) : TootApiResult? { val result = if(account.isMisskey) { val params = JSONObject().apply { @@ -408,6 +417,10 @@ class PostHelper( } } + if(scheduledAt != 0L) { + return TootApiResult("misskey has no scheduled status API") + } + } else { json.put( "status", @@ -456,6 +469,25 @@ class PostHelper( json.put("enquete_items", array) } + if(scheduledAt != 0L) { + if(! instance.versionGE(TootInstance.VERSION_2_7_0_rc1)) { + return TootApiResult("Mastodon pre-2.7.0 has no scheduled status API") + } + // UTCの日時を渡す + val c = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")) + c.timeInMillis = scheduledAt + val sv = String.format( + "%d-%02d-%02d %02d:%02d:%02d", + c.get(Calendar.YEAR), + c.get(Calendar.MONTH) + 1, + c.get(Calendar.DAY_OF_MONTH), + c.get(Calendar.HOUR_OF_DAY), + c.get(Calendar.MINUTE), + c.get(Calendar.SECOND) + ) + json.put("scheduled_at", sv) + } + } } catch(ex : JSONException) { log.trace(ex) @@ -478,6 +510,14 @@ class PostHelper( client.request("/api/v1/statuses", request_builder) } + val jsonObject = result?.jsonObject + + if(scheduledAt != 0L && jsonObject != null) { + // {"id":"3","scheduled_at":"2019-01-06T07:08:00.000Z","media_attachments":[]} + scheduledStatusSucceeded = true + return result + } + val status = parser.status( if(isMisskey) { result?.jsonObject?.optJSONObject("createdNote") ?: result?.jsonObject @@ -513,16 +553,23 @@ class PostHelper( } override fun handleResult(result : TootApiResult?) { - result ?: return // cancelled. - + result ?: return val status = this.status - if(status != null) { - // 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る - callback(account, status) - } else { - showToast(activity, true, result.error) + when { + status != null -> { + // 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る + callback.onPostComplete(account, status) + return + } + + scheduledStatusSucceeded -> { + callback.onScheduledPostComplete(account) + return + + } + + else -> showToast(activity, true, result.error) } - } }) ) @@ -711,13 +758,13 @@ class PostHelper( val dst = ArrayList() if(instance?.isNotEmpty() == true) { - + val custom_list = App1.custom_emoji_lister.getListWithAliases( instance, isMisskey, onEmojiListLoad ) - + if(custom_list != null) { for(item in custom_list) { diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/TypeAlias.kt b/app/src/main/java/jp/juggler/subwaytooter/util/TypeAlias.kt index b202416d..575857aa 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/util/TypeAlias.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/util/TypeAlias.kt @@ -2,8 +2,6 @@ package jp.juggler.subwaytooter.util import android.content.DialogInterface import jp.juggler.subwaytooter.api.TootApiResult -import jp.juggler.subwaytooter.api.entity.TootAccount -import jp.juggler.subwaytooter.api.entity.TootStatus import jp.juggler.subwaytooter.table.SavedAccount ///////////////////////////////////////////////////////////////// @@ -18,6 +16,5 @@ typealias SavedAccountCallback = (ai : SavedAccount) -> Unit typealias DialogInterfaceCallback = (dialog: DialogInterface) -> Unit -typealias PostCompleteCallback = (target_account : SavedAccount, status : TootStatus) -> Unit typealias ProgressResponseBodyCallback = (bytesRead : Long, bytesTotal : Long)->Unit diff --git a/app/src/main/res/drawable/ic_timer.xml b/app/src/main/res/drawable/ic_timer.xml new file mode 100644 index 00000000..f41be800 --- /dev/null +++ b/app/src/main/res/drawable/ic_timer.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_timer_dark.xml b/app/src/main/res/drawable/ic_timer_dark.xml new file mode 100644 index 00000000..704a3b8e --- /dev/null +++ b/app/src/main/res/drawable/ic_timer_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/act_post.xml b/app/src/main/res/layout/act_post.xml index 32a27a82..cd7a95d7 100644 --- a/app/src/main/res/layout/act_post.xml +++ b/app/src/main/res/layout/act_post.xml @@ -225,7 +225,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPostFormBackground" - android:layout_marginBottom="32dp" + > + + + + + + + + + + + + + + + + +