- (Misskey)返信のかわりに引用Renoteを作成できる(タンス側のバグがなければ)。

- (Misskey)返信と引用Renoteの際に元投稿をプレビューカードとして表示する。
- プレビューカードの表示にボーダー枠を追加。
This commit is contained in:
tateisu 2018-11-13 02:19:57 +09:00
parent 0a7d2c68bd
commit 25cd2e201b
15 changed files with 307 additions and 124 deletions

View File

@ -200,7 +200,7 @@ class ActMain : AppCompatActivity()
///////////////////////////////////////////////////////////////////////////////////////////////
private val link_click_listener : MyClickableSpanClickCallback = { viewClicked, span ->
internal val link_click_listener : MyClickableSpanClickCallback = { viewClicked, span ->
var view = viewClicked
var column : Column? = null
@ -353,8 +353,8 @@ class ActMain : AppCompatActivity()
// (カラム一覧画面のデフォルト選択位置に使われる)
val currentColumn : Int
get() = phoneTab(
{ pe -> pe.pager.currentItem },
{ _ -> - 1 }
{ it.pager.currentItem },
{ - 1 }
)
// 新しいカラムをどこに挿入するか
@ -369,8 +369,8 @@ class ActMain : AppCompatActivity()
// 新しいカラムをどこに挿入するか
private val defaultInsertPosition : Int
get() = phoneTab(
{ pe -> pe.pager.currentItem + 1 },
{ _ -> Integer.MAX_VALUE }
{ it.pager.currentItem + 1 },
{ Integer.MAX_VALUE }
)
private fun validateFloat(fv : Float) : Float {
@ -664,15 +664,15 @@ class ActMain : AppCompatActivity()
private fun performQuickPost(account : SavedAccount?) {
if(account == null) {
phoneTab({ env ->
// スマホモードなら表示中のカラムがあればそれで
val c = try{
val c = try {
app_state.column_list[env.pager.currentItem]
}catch(ex:Throwable){
} catch(ex : Throwable) {
null
}
if( c?.access_info?.isPseudo == false ) {
if(c?.access_info?.isPseudo == false) {
// 疑似アカウントではない
performQuickPost(c.access_info)
} else {
@ -684,7 +684,7 @@ class ActMain : AppCompatActivity()
message = getString(R.string.account_picker_toot)
) { ai -> performQuickPost(ai) }
}
}, { _ ->
}, {
// アカウント選択してやり直し
AccountPicker.pick(
this,
@ -702,7 +702,8 @@ class ActMain : AppCompatActivity()
post_helper.bNSFW = false
post_helper.in_reply_to_id = null
post_helper.attachment_list = null
post_helper.emojiMapCustom = App1.custom_emoji_lister.getMap(account.host,account.isMisskey)
post_helper.emojiMapCustom =
App1.custom_emoji_lister.getMap(account.host, account.isMisskey)
etQuickToot.hideKeyboard()
@ -1324,7 +1325,6 @@ class ActMain : AppCompatActivity()
env.tablet_pager.layoutManager = env.tablet_layout_manager
env.tablet_pager.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView : RecyclerView, newState : Int) {
super.onScrollStateChanged(recyclerView, newState)
@ -1423,7 +1423,7 @@ class ActMain : AppCompatActivity()
if(c == 0) {
Styler.setIconAttr(this, ivIcon, column.getIconAttrId(column.column_type))
} else {
Styler.setIconAttr(this, ivIcon, column.getIconAttrId(column.column_type),c)
Styler.setIconAttr(this, ivIcon, column.getIconAttrId(column.column_type), c)
}
//
@ -1460,7 +1460,7 @@ class ActMain : AppCompatActivity()
var slide_ratio = 0f
if(vr.first <= vr.last) {
val child = env.tablet_layout_manager.findViewByPosition(vr.first)
slide_ratio = Math.abs( (child?.left ?: 0) / nColumnWidth.toFloat())
slide_ratio = Math.abs((child?.left ?: 0) / nColumnWidth.toFloat())
}
llColumnStrip.setVisibleRange(vr.first, vr.last, slide_ratio)
@ -1505,9 +1505,9 @@ class ActMain : AppCompatActivity()
// ActOAuthCallbackで受け取ったUriを処理する
private fun handleIntentUri(uri : Uri) {
log.d("handleIntentUri ${uri}")
when(uri.scheme) {
"subwaytooter", "misskeyclientproto" -> return try {
handleOAuth2CallbackUri(uri)
@ -2031,7 +2031,7 @@ class ActMain : AppCompatActivity()
}
}
}, { _ ->
}, {
removeColumn(column)
if(! app_state.column_list.isEmpty() && page_delete > 0) {
@ -2061,8 +2061,8 @@ class ActMain : AppCompatActivity()
var lastColumnIndex = when(_lastColumnIndex) {
- 1 -> phoneTab(
{ pe -> pe.pager.currentItem },
{ _ -> 0 }
{ it.pager.currentItem },
{ 0 }
)
else -> _lastColumnIndex
}
@ -2272,9 +2272,9 @@ class ActMain : AppCompatActivity()
Styler.setIconAttr(this, btnMenu, R.attr.ic_hamburger)
Styler.setIconAttr(this, btnQuickToot, R.attr.btn_post)
} else {
Styler.setIconAttr(this, btnToot, R.attr.ic_edit,c)
Styler.setIconAttr(this, btnMenu, R.attr.ic_hamburger,c)
Styler.setIconAttr(this, btnQuickToot, R.attr.btn_post,c)
Styler.setIconAttr(this, btnToot, R.attr.ic_edit, c)
Styler.setIconAttr(this, btnMenu, R.attr.ic_hamburger, c)
Styler.setIconAttr(this, btnQuickToot, R.attr.btn_post, c)
}
c = footer_tab_bg_color
@ -2337,9 +2337,9 @@ class ActMain : AppCompatActivity()
for(i in 0 until env.tablet_layout_manager.childCount) {
val v = env.tablet_layout_manager.getChildAt(i)
val columnViewHolder =when(v){
null-> null
else->(env.tablet_pager.getChildViewHolder(v) as? TabletColumnViewHolder)?.columnViewHolder
val columnViewHolder = when(v) {
null -> null
else -> (env.tablet_pager.getChildViewHolder(v) as? TabletColumnViewHolder)?.columnViewHolder
}
if(columnViewHolder?.isColumnSettingShown == true) {
@ -2569,7 +2569,7 @@ class ActMain : AppCompatActivity()
ZipInputStream(FileInputStream(file)).use { zipStream ->
while(true) {
val entry = zipStream.nextEntry ?: break
++zipEntryCount
++ zipEntryCount
try {
//
val entryName = entry.name
@ -2581,13 +2581,13 @@ class ActMain : AppCompatActivity()
continue
}
if( AppDataExporter.restoreBackgroundImage(
if(AppDataExporter.restoreBackgroundImage(
this@ActMain,
newColumnList,
zipStream,
entryName
)
){
) {
continue
}
} finally {
@ -2597,12 +2597,12 @@ class ActMain : AppCompatActivity()
}
} catch(ex : Throwable) {
log.trace(ex)
if(zipEntryCount!=0) {
if(zipEntryCount != 0) {
showToast(this@ActMain, ex, "importAppData failed.")
}
}
// zipではなかった場合、zipEntryがない状態になる。例外はPH-1では出なかったが、出ても問題ないようにする。
if(zipEntryCount==0) {
if(zipEntryCount == 0) {
InputStreamReader(FileInputStream(file), "UTF-8").use { inStream ->
newColumnList = AppDataExporter.decodeAppData(
this@ActMain,
@ -2767,13 +2767,12 @@ class ActMain : AppCompatActivity()
}
}
private var dlgPrivacyPolicy : WeakReference<Dialog>?=null
private var dlgPrivacyPolicy : WeakReference<Dialog>? = null
private fun checkPrivacyPolicy() {
// 既に表示中かもしれない
if( dlgPrivacyPolicy?.get()?.isShowing == true) return
if(dlgPrivacyPolicy?.get()?.isShowing == true) return
val res_id = when(getString(R.string.language_code)) {
"ja" -> R.raw.privacy_policy_ja
@ -2783,29 +2782,27 @@ class ActMain : AppCompatActivity()
// プライバシーポリシーデータの読み込み
val bytes = loadRawResource(res_id)
if( bytes.isEmpty() ) return
if(bytes.isEmpty()) return
// 同意ずみなら表示しない
val digest = bytes.digestSHA256().encodeBase64Url()
if( digest == Pref.spAgreedPrivacyPolicyDigest(pref) ) return
if(digest == Pref.spAgreedPrivacyPolicyDigest(pref)) return
val dialog = AlertDialog.Builder(this)
.setTitle(R.string.privacy_policy)
.setMessage( bytes.decodeUTF8())
.setNegativeButton(R.string.cancel){_,_ ->
.setMessage(bytes.decodeUTF8())
.setNegativeButton(R.string.cancel) { _, _ ->
finish()
}
.setOnCancelListener{_->
.setOnCancelListener {
finish()
}
.setPositiveButton(R.string.agree){_,_ ->
pref.edit().put(Pref.spAgreedPrivacyPolicyDigest,digest).apply()
.setPositiveButton(R.string.agree) { _, _ ->
pref.edit().put(Pref.spAgreedPrivacyPolicyDigest, digest).apply()
}
.create()
dlgPrivacyPolicy = WeakReference(dialog)
dialog.show()
}
}

View File

@ -152,6 +152,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
internal const val DRAFT_REPLY_URL = "reply_url"
internal const val DRAFT_IS_ENQUETE = "is_enquete"
internal const val DRAFT_ENQUETE_ITEMS = "enquete_items"
internal const val DRAFT_QUOTED_RENOTE = "quotedRenote"
private const val STATE_MUSHROOM_INPUT = "mushroom_input"
private const val STATE_MUSHROOM_START = "mushroom_start"
@ -223,6 +224,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
internal lateinit var etContentWarning : MyEditText
internal lateinit var etContent : MyEditText
internal lateinit var cbQuoteRenote : CheckBox
internal lateinit var cbEnquete : CheckBox
private lateinit var llEnquete : View
internal lateinit var list_etChoice : List<MyEditText>
@ -703,14 +706,17 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
selectAccount(null)
}
updateContentWarning()
showMediaAttachment()
showVisibility()
updateTextCount()
showReplyTo()
showEnquete()
showQuotedRenote()
}
override fun onDestroy() {
post_helper.onDestroy()
@ -765,6 +771,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
updateTextCount()
showReplyTo()
showEnquete()
showQuotedRenote()
}
private fun appendContentText(
@ -853,6 +860,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
etContentWarning = findViewById(R.id.etContentWarning)
etContent = findViewById(R.id.etContent)
cbQuoteRenote= findViewById(R.id.cbQuoteRenote)
cbEnquete = findViewById(R.id.cbEnquete)
llEnquete = findViewById(R.id.llEnquete)
@ -1128,6 +1137,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
log.trace(ex)
}
showVisibility()
showQuotedRenote()
}
@SuppressLint("StaticFieldLeak")
@ -1974,6 +1984,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
post_helper.redraft_status_id = redraft_status_id
post_helper.useQuotedRenote = cbQuoteRenote.isChecked
post_helper.post(account) { target_account, status ->
val data = Intent()
data.putExtra(EXTRA_POSTED_ACCT, target_account.acct)
@ -1986,6 +1998,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
}
}
private fun showQuotedRenote() {
val isReply = in_reply_to_id != null
val isMisskey = account?.isMisskey == true
cbQuoteRenote.visibility = if( isReply && isMisskey ) View.VISIBLE else View.GONE
}
internal fun showReplyTo() {
if(in_reply_to_id == null) {
llReply.visibility = View.GONE
@ -2008,6 +2026,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
in_reply_to_image = null
in_reply_to_url = null
showReplyTo()
showQuotedRenote()
}
private fun saveDraft() {
@ -2054,7 +2073,9 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
json.put(DRAFT_REPLY_IMAGE, in_reply_to_image)
json.put(DRAFT_REPLY_URL, in_reply_to_url)
json.put(DRAFT_QUOTED_RENOTE,cbQuoteRenote.isChecked)
json.put(DRAFT_IS_ENQUETE, isEnquete)
val array = JSONArray()
for(s in str_choice) {
array.put(s)
@ -2206,6 +2227,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
val draft_visibility = TootVisibility
.parseSavedVisibility(draft.parseString(DRAFT_VISIBILITY))
val evEmoji = DecodeOptions(this@ActPost, decodeEmoji = true).decodeEmoji(content)
etContent.setText(evEmoji)
etContent.setSelection(evEmoji.length)
@ -2215,6 +2237,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
cbNSFW.isChecked = nsfw_checked
if(draft_visibility != null) this@ActPost.visibility = draft_visibility
cbQuoteRenote.isChecked = draft.optBoolean(DRAFT_QUOTED_RENOTE)
cbEnquete.isChecked = draft.optBoolean(DRAFT_IS_ENQUETE, false)
val array = draft.optJSONArray(DRAFT_ENQUETE_ITEMS)
if(array != null) {
@ -2254,6 +2277,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
in_reply_to_image = reply_image
in_reply_to_url = reply_url
}
updateContentWarning()
showMediaAttachment()
@ -2261,6 +2285,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
updateTextCount()
showReplyTo()
showEnquete()
showQuotedRenote()
if(! list_warning.isEmpty()) {
val sb = StringBuilder()

View File

@ -26,6 +26,7 @@ import jp.juggler.subwaytooter.api.TootTaskRunner
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.dialog.DlgConfirm
import jp.juggler.subwaytooter.drawable.PreviewCardBorder
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.table.*
@ -120,12 +121,13 @@ internal class ItemViewHolder(
private lateinit var tvFilterDetail : TextView
private lateinit var tvMediaDescription : TextView
private lateinit var llCardOuter : View
private lateinit var tvCardText : TextView
private lateinit var ivCardImage : MyNetworkImageView
private lateinit var llExtra : LinearLayout
private lateinit var llConversationIcons : View
private lateinit var ivConversationIcon1 : MyNetworkImageView
private lateinit var ivConversationIcon2 : MyNetworkImageView
@ -134,8 +136,6 @@ internal class ItemViewHolder(
private lateinit var tvConversationIconsMore : TextView
private lateinit var tvConversationParticipants : TextView
private lateinit var tvApplication : TextView
private lateinit var tvMessageHolder : TextView
@ -181,7 +181,8 @@ internal class ItemViewHolder(
btnFollow.setOnClickListener(this)
btnFollow.setOnLongClickListener(this)
ivCardImage.setOnClickListener(this)
llCardOuter.setOnClickListener(this)
llCardOuter.setOnLongClickListener(this)
ivThumbnail.setOnClickListener(this)
@ -263,6 +264,13 @@ internal class ItemViewHolder(
this.reply_invalidator = NetworkEmojiInvalidator(activity.handler, tvReply)
this.follow_invalidator = NetworkEmojiInvalidator(activity.handler, tvFollowerName)
this.name_invalidator = NetworkEmojiInvalidator(activity.handler, tvName)
val cardBackground = llCardOuter.background
if(cardBackground is PreviewCardBorder) {
val density = activity.density
cardBackground.round = (density * 8f)
cardBackground.width = (density * 1f)
}
}
fun onViewRecycled() {
@ -375,6 +383,7 @@ internal class ItemViewHolder(
llTrendTag.visibility = View.GONE
llFilter.visibility = View.GONE
tvMediaDescription.visibility = View.GONE
llCardOuter.visibility = View.GONE
tvCardText.visibility = View.GONE
ivCardImage.visibility = View.GONE
llConversationIcons.visibility = View.GONE
@ -402,6 +411,12 @@ internal class ItemViewHolder(
tvConversationIconsMore.setTextColor(c)
tvConversationParticipants.setTextColor(c)
val cardBackground = llCardOuter.background
if(cardBackground is PreviewCardBorder) {
cardBackground.color = c
}
c = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(
activity,
R.attr.colorTimeSmall
@ -484,12 +499,10 @@ internal class ItemViewHolder(
}
extra_invalidator_list.clear()
}
private fun showConversationIcons(cs:TootConversationSummary) {
private fun showConversationIcons(cs : TootConversationSummary) {
val accounts = cs.accounts
val last_account_id = cs.last_status.account.id
val accountsOther = cs.accounts.filter { it.get().id != last_account_id }
@ -498,17 +511,17 @@ internal class ItemViewHolder(
val size = accountsOther.size
tvConversationParticipants.text =if(size <= 1){
tvConversationParticipants.text = if(size <= 1) {
activity.getString(R.string.conversation_to)
}else{
} else {
activity.getString(R.string.participants)
}
fun showIcon(iv : MyNetworkImageView, idx : Int) {
val bShown = idx < size
iv.visibility = if(bShown) View.VISIBLE else View.GONE
if(!bShown) return
if(! bShown) return
val who = accountsOther[idx].get()
iv.setImageUrl(
activity.pref,
@ -527,14 +540,14 @@ internal class ItemViewHolder(
else -> activity.getString(R.string.participants_and_more)
}
}
if( cs.last_status.in_reply_to_id != null ) {
if(cs.last_status.in_reply_to_id != null) {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = activity.getString(R.string.show_conversation)
}
}
private fun openConversationSummary(){
private fun openConversationSummary() {
val cs = item as? TootConversationSummary ?: return
if(cs.unread) {
@ -545,7 +558,7 @@ internal class ItemViewHolder(
reset = true
)
// 未読フラグのクリアをサーバに送る
Action_Toot.clearConversationUnread(activity,access_info,cs)
Action_Toot.clearConversationUnread(activity, access_info, cs)
}
Action_Toot.conversation(
@ -936,7 +949,7 @@ internal class ItemViewHolder(
// }
var content = status.decoded_content
// ニコフレのアンケートの表示
val enquete = status.enquete
if(enquete != null) {
@ -958,10 +971,7 @@ internal class ItemViewHolder(
}
// カードの表示(会話ビューのみ)
val card = status.card
if(card != null) {
showPreviewCard(activity, card)
}
showPreviewCard(activity, status)
// if( status.decoded_tags == null ){
// tvTags.setVisibility( View.GONE );
@ -1048,7 +1058,7 @@ internal class ItemViewHolder(
setMedia(media_attachments, sb, ivMedia2, 1)
setMedia(media_attachments, sb, ivMedia3, 2)
setMedia(media_attachments, sb, ivMedia4, 3)
if( sb. isNotEmpty()){
if(sb.isNotEmpty()) {
tvMediaDescription.visibility = View.VISIBLE
tvMediaDescription.text = sb
}
@ -1326,9 +1336,10 @@ internal class ItemViewHolder(
if(column.content_color != 0) column.content_color else content_color_default
tv.setTextColor(c)
if( ta.description ?. isNotEmpty() == true){
if( sbDesc.isNotEmpty()) sbDesc.append("\n")
val desc = activity.getString(R.string.media_description, idx + 1, ta.description)
if(ta.description?.isNotEmpty() == true) {
if(sbDesc.isNotEmpty()) sbDesc.append("\n")
val desc =
activity.getString(R.string.media_description, idx + 1, ta.description)
sbDesc.append(desc)
}
}
@ -1353,8 +1364,6 @@ internal class ItemViewHolder(
}
private var boostedAction : () -> Unit = defaultBoostedAction
override fun onClick(v : View) {
val pos = activity.nextPosition(column)
@ -1516,8 +1525,26 @@ internal class ItemViewHolder(
openFilterMenu(item)
}
ivCardImage-> status_showing?.card?.url?.let { url ->
if(url.isNotEmpty()) App1.openCustomTab(activity, url)
llCardOuter -> status_showing?.card?.let { card ->
val originalStatus = card.originalStatus
if(originalStatus != null) {
Action_Toot.conversation(
activity,
activity.nextPosition(column),
access_info,
originalStatus
)
} else {
val url = card.url
if(url?.isNotEmpty() == true) {
ChromeTabOpener(
activity,
pos,
url,
accessInfo = access_info
).open()
}
}
}
llConversationIcons -> openConversationSummary()
@ -1593,6 +1620,12 @@ internal class ItemViewHolder(
return true
}
llCardOuter -> Action_Toot.conversationOtherInstance(
activity,
activity.nextPosition(column),
status_showing?.card?.originalStatus
)
btnSearchTag, llTrendTag -> {
val item = this.item
when(item) {
@ -1666,15 +1699,35 @@ internal class ItemViewHolder(
}
}
private fun showPreviewCard(activity : ActMain, card : TootCard) {
private fun showPreviewCard(activity : ActMain, status : TootStatus) {
val card = status.card ?: return
// 会話カラムで返信ステータスなら捏造したカードを表示しない
if(column.column_type == Column.TYPE_CONVERSATION
&& card.originalStatus != null
&& status.reply != null
) {
return
}
llCardOuter.visibility = View.VISIBLE
val sb = StringBuilder()
addLinkAndCaption(sb, activity.getString(R.string.card_header_card), card.url, card.title)
addLinkAndCaption(
sb,
activity.getString(R.string.card_header_card),
card.url,
card.title
)
addLinkAndCaption(
sb,
activity.getString(R.string.card_header_author),
card.author_url,
card.author_name
)
addLinkAndCaption(
sb,
activity.getString(R.string.card_header_provider),
@ -1688,12 +1741,20 @@ internal class ItemViewHolder(
val limit = Pref.spCardDescriptionLength.toInt(activity.pref)
sb.append(HTMLDecoder.encodeEntity(ellipsize(description,if( limit <= 0 ) 64 else limit)))
sb.append(
HTMLDecoder.encodeEntity(
ellipsize(
description,
if(limit <= 0) 64 else limit
)
)
)
}
if( sb.isNotEmpty() ){
val text = DecodeOptions(activity, access_info).decodeHTML(sb.toString())
if( text.isNotEmpty()){
if(sb.isNotEmpty()) {
val text =
DecodeOptions(activity, access_info, forceHtml = true).decodeHTML(sb.toString())
if(text.isNotEmpty()) {
tvCardText.visibility = View.VISIBLE
tvCardText.text = text
}
@ -1711,11 +1772,11 @@ internal class ItemViewHolder(
}
}
private fun ellipsize(src:String,limit:Int):String{
return if( src.codePointCount(0,src.length) <= limit ) {
private fun ellipsize(src : String, limit : Int) : String {
return if(src.codePointCount(0, src.length) <= limit) {
src
}else {
"${src.substring(0, src.offsetByCodePoints(0,limit))}"
} else {
"${src.substring(0, src.offsetByCodePoints(0, limit))}"
}
}
@ -1899,7 +1960,7 @@ internal class ItemViewHolder(
}
if((result.response?.code() ?: - 1) in 200 until 300) {
if(status.increaseReaction(code,true,"addReaction")){
if(status.increaseReaction(code, true, "addReaction")) {
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
list_adapter.notifyChange(reason = "addReaction complete", reset = true)
}
@ -2021,14 +2082,13 @@ internal class ItemViewHolder(
val data = result.jsonObject
if(data != null) {
if(accessInfo.isMisskey) {
if( enquete.increaseVote(activity,idx,true) ){
if(enquete.increaseVote(activity, idx, true)) {
showToast(context, false, R.string.enquete_voted)
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
list_adapter.notifyChange(reason = "onClickEnqueteChoice", reset = true)
}
} else {
val message = data.parseString("message") ?: "?"
val valid = data.optBoolean("valid")
@ -2499,25 +2559,35 @@ internal class ItemViewHolder(
}
}
tvMediaDescription = textView{}.lparams(matchParent, wrapContent)
tvMediaDescription = textView {}.lparams(matchParent, wrapContent)
tvCardText = textView{
}.lparams(matchParent, wrapContent){
topMargin = dip(3)
}
ivCardImage = myNetworkImageView {
llCardOuter = verticalLayout {
lparams(matchParent, wrapContent) {
topMargin = dip(3)
startMargin = dip(12)
endMargin = dip(6)
}
padding = dip(3)
bottomPadding = dip(6)
contentDescription = context.getString(R.string.thumbnail)
background = PreviewCardBorder()
scaleType = if(Pref.bpDontCropMediaThumb(App1.pref))
ImageView.ScaleType.FIT_CENTER
else
ImageView.ScaleType.CENTER_CROP
tvCardText = textView {
}.lparams(matchParent, wrapContent) {
}
}.lparams(matchParent, activity.app_state.media_thumb_height) {
topMargin = dip(3)
ivCardImage = myNetworkImageView {
contentDescription = context.getString(R.string.thumbnail)
scaleType = if(Pref.bpDontCropMediaThumb(App1.pref))
ImageView.ScaleType.FIT_CENTER
else
ImageView.ScaleType.CENTER_CROP
}.lparams(matchParent, activity.app_state.media_thumb_height) {
topMargin = dip(3)
}
}
@ -2526,7 +2596,6 @@ internal class ItemViewHolder(
topMargin = dip(0)
}
}
}
// button bar
@ -2560,34 +2629,34 @@ internal class ItemViewHolder(
tvConversationParticipants = textView {
text = context.getString(R.string.participants)
}.lparams(wrapContent,wrapContent){
}.lparams(wrapContent, wrapContent) {
endMargin = dip(3)
}
ivConversationIcon1 = myNetworkImageView {
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(24),dip(24)) {
}.lparams(dip(24), dip(24)) {
endMargin = dip(3)
}
ivConversationIcon2 = myNetworkImageView {
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(24),dip(24)) {
}.lparams(dip(24), dip(24)) {
endMargin = dip(3)
}
ivConversationIcon3 = myNetworkImageView {
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(24),dip(24)) {
}.lparams(dip(24), dip(24)) {
endMargin = dip(3)
}
ivConversationIcon4 = myNetworkImageView {
scaleType = ImageView.ScaleType.CENTER_CROP
}.lparams(dip(24),dip(24)) {
}.lparams(dip(24), dip(24)) {
endMargin = dip(3)
}
tvConversationIconsMore = textView {
}.lparams(wrapContent,wrapContent)
}.lparams(wrapContent, wrapContent)
}
llSearchTag = linearLayout {

View File

@ -179,7 +179,7 @@ internal class ViewHolderHeaderProfile(
btnFollow.setImageDrawable(null)
tvRemoteProfileWarning.visibility = View.GONE
} else {
tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
tvCreated.text = TootStatus.formatTime(tvCreated.context, (whoDetail?:who).time_created_at, true)
ivBackground.setImageUrl(
activity.pref,
0f,

View File

@ -181,7 +181,6 @@ object Action_Follow {
override fun background(client : TootApiClient) : TootApiResult? {
var result : TootApiResult?
val parser = TootParser(activity, access_info)
if(access_info.isMisskey) {

View File

@ -1,8 +1,10 @@
package jp.juggler.subwaytooter.api.entity
import org.json.JSONObject
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.util.HTMLDecoder
import jp.juggler.subwaytooter.util.filterNotEmpty
import jp.juggler.subwaytooter.util.parseString
import org.json.JSONObject
class TootCard(
@ -19,10 +21,12 @@ class TootCard(
val image : String?,
val type : String?,
val author_name : String?,
val author_url : String?,
val provider_name : String?,
val provider_url : String?
val author_name : String? =null,
val author_url : String? =null,
val provider_name : String? =null,
val provider_url : String? =null,
val originalStatus :TootStatus? =null
) {
constructor(src : JSONObject) : this(
@ -38,4 +42,17 @@ class TootCard(
provider_url = src.parseString("provider_url")
)
constructor(parser: TootParser, src:TootStatus) :this(
originalStatus = src,
url = src.url,
title = "${src.account.display_name} @${parser.linkHelper .getFullAcct(src.account.acct)}",
description = if( parser.serviceType==ServiceType.MISSKEY){
src.spoiler_text.filterNotEmpty() ?: src.content
}else{
src.spoiler_text.filterNotEmpty() ?: HTMLDecoder.encodeEntity( src.content ?: "")
},
image = src.media_attachments ?. firstOrNull() ?. urlForThumbnail ?: src.account.avatar_static,
type = "photo"
)
}

View File

@ -312,6 +312,18 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
this.deletedAt = src.parseString("deletedAt")
this.time_deleted_at = parseTime(deletedAt)
if( card == null) {
if(reblog != null && hasAnyContent() ) {
// 引用Renoteにプレビューカードをでっちあげる
card = TootCard(parser, reblog)
} else if(reply != null ) {
// 返信にプレビューカードをでっちあげる
card = TootCard(parser, reply!! )
}
}
} else {
misskeyVisibleIds = null
reply = null

View File

@ -0,0 +1,39 @@
package jp.juggler.subwaytooter.drawable
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
class PreviewCardBorder : Drawable() {
var color = 0
var round = 0f
var width = 1f
private val paint = Paint()
override fun draw(canvas : Canvas) {
paint.isAntiAlias = true
paint.color = color
paint.style = Paint.Style.STROKE
paint.strokeWidth = width
val bounds = this.bounds
val left = bounds.left + width/2
val right = bounds.right -width/2
val top = bounds.top + width/2
val bottom = bounds.bottom -width/2
canvas.drawRoundRect( left,top,right,bottom,round,round,paint )
}
override fun getOpacity() : Int = PixelFormat.TRANSLUCENT
override fun setAlpha(alpha : Int) =Unit
override fun setColorFilter(colorFilter : ColorFilter?) =Unit
}

View File

@ -21,7 +21,8 @@ class DecodeOptions(
var emojiMapProfile : HashMap<String, NicoProfileEmoji>? = null,
var highlightTrie : WordTrieTree? = null,
var unwrapEmojiImageTag :Boolean = false,
var enlargeCustomEmoji :Float = 1f
var enlargeCustomEmoji :Float = 1f,
var forceHtml : Boolean = false // force use HTML instead of Misskey Markdown
) {
internal fun isMediaAttachment(url : String?) : Boolean {

View File

@ -468,7 +468,7 @@ object HTMLDecoder {
fun decodeHTML(options : DecodeOptions, src : String?) : SpannableStringBuilder {
if( options.linkHelper?.isMisskey == true){
if( options.linkHelper?.isMisskey == true && !options.forceHtml ){
return MisskeyMarkdownDecoder.decodeMarkdown(options,src)
}

View File

@ -77,6 +77,7 @@ class PostHelper(
var enquete_items : ArrayList<String>? = null
var emojiMapCustom : HashMap<String, CustomEmoji>? = null
var redraft_status_id : EntityId? = null
var useQuotedRenote : Boolean = false
private var last_post_tapped : Long = 0L
@ -358,7 +359,11 @@ class PostHelper(
}
if(in_reply_to_id != null) {
json.put("replyId", in_reply_to_id.toString())
if( useQuotedRenote){
json.put("renoteId", in_reply_to_id.toString())
}else{
json.put("replyId", in_reply_to_id.toString())
}
}
json.put("viaMobile", true)
@ -470,8 +475,9 @@ class PostHelper(
}
result = if(isMisskey) {
log.d("misskey json %s",body_string)
client.request("/api/notes/create", request_builder)
// TODO {"error":{}} が返ってきた時にどう扱えばいい?
} else {
client.request("/api/v1/statuses", request_builder)
}
@ -549,7 +555,7 @@ class PostHelper(
private var isMisskey = false
private val onEmojiListLoad : (list : ArrayList<CustomEmoji>) -> Unit =
{ _ : ArrayList<CustomEmoji> ->
{
val popup = this@PostHelper.popup
if(popup?.isShowing == true) proc_text_changed.run()
}
@ -740,9 +746,9 @@ class PostHelper(
val remain = limit - code_list.size
if(remain > 0) {
val s = src.substring(last_colon + 1, end).toLowerCase().replace('-', '_')
val src = EmojiDecoder.searchShortCode(activity, s, remain)
log.d("checkEmoji: search for %s, result=%d", s, src.size)
code_list.addAll(src)
val matches = EmojiDecoder.searchShortCode(activity, s, remain)
log.d("checkEmoji: search for %s, result=%d", s, matches.size)
code_list.addAll(matches)
}

View File

@ -488,6 +488,12 @@ fun String?.optInt() : Int? {
}
}
fun String?.filterNotEmpty() :String? = when{
this==null -> null
this.isEmpty() -> null
else->this
}
//fun String.ellipsize(max : Int) = if(this.length > max) this.substring(0, max - 1) + "…" else this
//
//fun String.toCamelCase() : String {

View File

@ -83,6 +83,14 @@
android:src="?attr/btn_close"
/>
</LinearLayout>
<CheckBox
android:id="@+id/cbQuoteRenote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/make_quote_renote"
/>
</LinearLayout>
<LinearLayout
@ -217,6 +225,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPostFormBackground"
android:layout_marginBottom="32dp"
>
<jp.juggler.subwaytooter.view.MyEditText
@ -231,11 +240,12 @@
</FrameLayout>
<CheckBox
android:id="@+id/cbEnquete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/make_enquete"
/>

View File

@ -784,5 +784,6 @@
<string name="participants">会話の参加者:</string>
<string name="participants_and_more">…他</string>
<string name="conversation_to">送り先:</string>
<string name="make_quote_renote">引用Renoteにする</string>
</resources>

View File

@ -803,5 +803,6 @@
<string name="participants">Conversation participants:</string>
<string name="participants_and_more">… and more</string>
<string name="conversation_to">To:</string>
<string name="make_quote_renote">Use \"Quoted Renote\"</string>
</resources>