From 4d9c9068d715754302544bb88f96de324336cd63 Mon Sep 17 00:00:00 2001 From: tateisu Date: Tue, 21 Aug 2018 10:53:52 +0900 Subject: [PATCH] =?UTF-8?q?v269=E3=80=82=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=81=A8?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 4 +- .../jp/juggler/subwaytooter/ItemViewHolder.kt | 217 ++++++++++++++++-- .../java/jp/juggler/subwaytooter/Styler.kt | 20 +- .../api/entity/MisskeyReaction.kt | 52 +++-- .../subwaytooter/api/entity/TootStatus.kt | 47 ++-- .../subwaytooter/dialog/ActionsDialog.kt | 2 +- app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 3 +- app/src/main/res/values/strings.xml | 1 + 9 files changed, 278 insertions(+), 69 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3dc8a818..638af16c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { minSdkVersion 21 targetSdkVersion 27 - versionCode 268 - versionName "2.6.8" + versionCode 269 + versionName "2.6.9" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // https://stackoverflow.com/questions/47791227/java-lang-illegalstateexception-dex-archives-setting-dex-extension-only-for diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt index 4b621e88..0314e5af 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt @@ -15,6 +15,9 @@ import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.* +import com.google.android.flexbox.AlignItems +import com.google.android.flexbox.FlexWrap +import com.google.android.flexbox.FlexboxLayout import jp.juggler.subwaytooter.action.* import java.util.ArrayList @@ -46,7 +49,6 @@ internal class ItemViewHolder( companion object { private val log = LogCategory("ItemViewHolder") - } val viewRoot : View @@ -246,7 +248,7 @@ internal class ItemViewHolder( bSimpleList : Boolean, item : TimelineItem ) { - + this.list_adapter = list_adapter this.column = column this.bSimpleList = bSimpleList @@ -406,8 +408,8 @@ internal class ItemViewHolder( is TootList -> showList(item) is TootMessageHolder -> showMessageHolder(item) - - // TootTrendTag の後に TootTagを判定すること + + // TootTrendTag の後に TootTagを判定すること is TootTrendTag -> showTrendTag(item) is TootTag -> showSearchTag(item) @@ -596,8 +598,13 @@ internal class ItemViewHolder( this.status_showing = status llStatus.visibility = View.VISIBLE - if( status.conversation_main) { - this.viewRoot.setBackgroundColor( (Styler.getAttributeColor(activity,R.attr.colorImageButtonAccent) and 0xffffff ) or 0x20000000) + if(status.conversation_main) { + this.viewRoot.setBackgroundColor( + (Styler.getAttributeColor( + activity, + R.attr.colorImageButtonAccent + ) and 0xffffff) or 0x20000000 + ) } showStatusTime(activity, tvTime, who = status.account, status = status) @@ -726,6 +733,8 @@ internal class ItemViewHolder( setMedia(ivMedia4, status, media_attachments, 3) } + makeReactionsView(status.reactionCounts) + buttons_for_status?.bind(status, (item as? TootNotification)) val application = status.application @@ -770,12 +779,12 @@ internal class ItemViewHolder( if(sb.isNotEmpty()) sb.append('\u200B') sb.appendColorShadeIcon(activity, R.drawable.ic_shield, "admin") } - + if(status.account.isCat) { if(sb.isNotEmpty()) sb.append('\u200B') sb.appendColorShadeIcon(activity, R.drawable.ic_cat, "cat") } - + // botマーク if(status.account.bot) { if(sb.isNotEmpty()) sb.append('\u200B') @@ -805,11 +814,18 @@ internal class ItemViewHolder( } // visibility - val visIconAttrId = Styler.getVisibilityIconAttr(access_info.isMisskey,status.visibility) + val visIconAttrId = + Styler.getVisibilityIconAttr(access_info.isMisskey, status.visibility) if(R.attr.ic_public != visIconAttrId) { if(sb.isNotEmpty()) sb.append('\u200B') val start = sb.length - sb.append(Styler.getVisibilityString(activity,access_info.isMisskey,status.visibility)) + sb.append( + Styler.getVisibilityString( + activity, + access_info.isMisskey, + status.visibility + ) + ) val end = sb.length val iconResId = Styler.getAttributeResourceId(activity, visIconAttrId) sb.setSpan( @@ -1009,8 +1025,19 @@ internal class ItemViewHolder( ivThumbnail -> status_account?.let { whoRef -> when { - access_info.isMisskey -> Action_User.profileLocal(activity, pos, access_info, whoRef.get()) - access_info.isPseudo -> DlgContextMenu(activity, column, whoRef, null, notification).show() + access_info.isMisskey -> Action_User.profileLocal( + activity, + pos, + access_info, + whoRef.get() + ) + access_info.isPseudo -> DlgContextMenu( + activity, + column, + whoRef, + null, + notification + ).show() else -> Action_User.profileLocal(activity, pos, access_info, whoRef.get()) } } @@ -1194,16 +1221,16 @@ internal class ItemViewHolder( btnSearchTag, llTrendTag -> { val item = this.item when(item) { - // is TootGap -> column.startGap(item) - // - // is TootDomainBlock -> { - // val domain = item.domain - // AlertDialog.Builder(activity) - // .setMessage(activity.getString(R.string.confirm_unblock_domain, domain)) - // .setNegativeButton(R.string.cancel, null) - // .setPositiveButton(R.string.ok) { _, _ -> Action_Instance.blockDomain(activity, access_info, domain, false) } - // .show() - // } + // is TootGap -> column.startGap(item) + // + // is TootDomainBlock -> { + // val domain = item.domain + // AlertDialog.Builder(activity) + // .setMessage(activity.getString(R.string.confirm_unblock_domain, domain)) + // .setNegativeButton(R.string.cancel, null) + // .setPositiveButton(R.string.ok) { _, _ -> Action_Instance.blockDomain(activity, access_info, domain, false) } + // .show() + // } is TootTag -> { // search_tag は#を含まない @@ -1246,7 +1273,7 @@ internal class ItemViewHolder( is TootAttachment -> { if(Pref.bpUseInternalMediaViewer(App1.pref)) { // 内蔵メディアビューア - val serviceType = when(access_info.isMisskey){ + val serviceType = when(access_info.isMisskey) { true -> ServiceType.MISSKEY else -> ServiceType.MASTODON } @@ -1362,6 +1389,148 @@ internal class ItemViewHolder( } + private fun makeReactionsView(reactionsCount : HashMap?) { + + if( ! access_info.isMisskey) return + + // reactionsCount?:return +// MisskeyReaction.values().find { +// val c = reactionsCount[it.shortcode] +// c != null && c > 0 +// } ?: return + + + + val density = activity.resources.displayMetrics.density + val compoundPadding = (density * 0.5f + 0.5f).toInt() + val endMargin = (density * 3f + 0.5f).toInt() + val paddingHorizontal = (density * 4f + 0.5f).toInt() + val btnHeight = (density * 40f + 0.5f).toInt() + + val box = FlexboxLayout(activity) + val boxLp = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + box.layoutParams = boxLp + boxLp.topMargin = (0.5f + density * 3f).toInt() + box.flexWrap = FlexWrap.WRAP + box.alignItems = AlignItems.FLEX_START + + // +ボタン + run{ + val b = ImageButton(activity) + val blp = FlexboxLayout.LayoutParams( + btnHeight, + btnHeight + ) + b.layoutParams = blp + blp.endMargin = endMargin + b.background = ContextCompat.getDrawable( + activity, + R.drawable.btn_bg_transparent + ) + b.minimumWidth = (density * 40f + 0.5f).toInt() + b.contentDescription = activity.getString(R.string.reaction_add) + b.imageResource = Styler.getAttributeResourceId(activity,R.attr.ic_add) + b.padding= paddingHorizontal + b.setOnClickListener{ addReaction(status_showing,null) } + box.addView(b) + } + var lastButton : Button? = null + for(mr in MisskeyReaction.values()) { + val count = reactionsCount?.get(mr.shortcode) + if(count == null || count <= 0) continue + val b = Button(activity) + val blp = FlexboxLayout.LayoutParams( + FlexboxLayout.LayoutParams.WRAP_CONTENT, + btnHeight + ) + b.layoutParams = blp + blp.endMargin = endMargin + b.background = ContextCompat.getDrawable( + activity, + R.drawable.btn_bg_transparent + ) + b.minWidthCompat = (density * 40f + 0.5f).toInt() + b.text = count.toString() + b.compoundDrawablePadding = compoundPadding + b.padding= paddingHorizontal + b.tag = mr.shortcode + b.setOnClickListener{addReaction(status_showing,it.tag as? String) } + val d = ContextCompat.getDrawable(activity, mr.drawableId) + b.setCompoundDrawablesRelativeWithIntrinsicBounds(d, null, null, null) + box.addView(b) + lastButton = b + } + + if( lastButton != null ){ + val lp = lastButton.layoutParams + if( lp is ViewGroup.MarginLayoutParams){ + lp.endMargin = 0 + } + } + + + + llExtra.addView(box) + } + + private fun addReaction(status:TootStatus?,code : String?) { + status?:return + + if( access_info.isPseudo || !access_info.isMisskey) return + + if(code == null ){ + val ad = ActionsDialog() + for( mr in MisskeyReaction.values()){ + val code= mr.shortcode + val sb = SpannableStringBuilder() + .appendDrawableIcon(activity,mr.drawableId," ") + .append(' ') + .append(mr.shortcode) + ad.addAction(sb){ + addReaction(status,code) + } + } + ad.show(activity) + return + } + + TootTaskRunner(activity,progress_style = TootTaskRunner.PROGRESS_NONE).run(access_info,object :TootTask{ + override fun background(client : TootApiClient) : TootApiResult? { + val params = access_info.putMisskeyApiToken(JSONObject()) + .put("noteId",status.id.toString()) + .put("reaction",code) + val result = client.request("/api/notes/reactions/create",params.toPostRequestBuilder()) + // 成功すると204 no content + return result + } + + override fun handleResult(result : TootApiResult?) { + result?: return + + val error = result.error + if( error!=null){ + showToast(activity,false,error) + return + } + + if( (result.response?.code()?:-1) in 200 until 300 ){ + if( status.reactionCounts == null ){ + status.reactionCounts = HashMap() + } + val count = status.reactionCounts?.get(code) ?: 0 + status.reactionCounts?.put(code,count+1) + // 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する + list_adapter.notifyChange(reason = "addReaction complete", reset = true) + } + + } + + }) + } + private fun makeEnqueteChoiceView( enquete : NicoEnquete, now : Long, @@ -1903,7 +2072,7 @@ internal class ItemViewHolder( } val marginBetween = dip(2) - val compoundPadding= dip(0.5f) + val compoundPadding = dip(0.5f) btnConversation = imageButton { diff --git a/app/src/main/java/jp/juggler/subwaytooter/Styler.kt b/app/src/main/java/jp/juggler/subwaytooter/Styler.kt index b4be90d9..6e100bb3 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Styler.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/Styler.kt @@ -291,7 +291,7 @@ fun SpannableStringBuilder.appendColorShadeIcon( context:Context, drawable_id:Int, text:String -){ +):SpannableStringBuilder{ val start = this.length this.append(text) val end = this.length @@ -301,4 +301,22 @@ fun SpannableStringBuilder.appendColorShadeIcon( end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE ) + return this +} + +fun SpannableStringBuilder.appendDrawableIcon( + context:Context, + drawable_id:Int, + text:String +):SpannableStringBuilder{ + val start = this.length + this.append(text) + val end = this.length + this.setSpan( + EmojiImageSpan(context, drawable_id), + start, + end, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + return this } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyReaction.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyReaction.kt index 00767d84..920d7c7a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyReaction.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/MisskeyReaction.kt @@ -1,24 +1,28 @@ -//package jp.juggler.subwaytooter.api.entity -// -//enum class MisskeyReaction(val name:String,val drawableId:Int){ -// -// -// Like("like"), -// Love("love"), -// Laugh("laugh"), -// Hmm("hmm"), -// Surprise("surprise"), -// Congrats("congrats"), -// Angry("angry"), -// Confused("confused"), -// Rip("rip"), -// Pudding("pudding") ; -// -// companion object { -// val map: HashMap by lazy { -// HashMap().apply { -// -// } -// } -// } -//} \ No newline at end of file +package jp.juggler.subwaytooter.api.entity + +import jp.juggler.subwaytooter.R + +enum class MisskeyReaction(val shortcode:String,val drawableId:Int){ + + + Like("like", R.drawable.emj_1f44d), + Love("love",R.drawable.emj_2665), + Laugh("laugh",R.drawable.emj_1f606), + Hmm("hmm", R.drawable.emj_1f914), + Surprise("surprise",R.drawable.emj_1f62e), + Congrats("congrats",R.drawable.emj_1f389), + Angry("angry",R.drawable.emj_1f4a2), + Confused("confused",R.drawable.emj_1f625), + Rip("rip", R.drawable.emj_1f607), + Pudding("pudding", R.drawable.emj_1f36e) ; + + companion object { + val shortcodeMap: HashMap by lazy { + HashMap().apply { + for( e in MisskeyReaction.values()){ + put( e.shortcode,e) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt index aefe4a96..5243863e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt @@ -140,6 +140,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() { var viaMobile : Boolean = false + var reactionCounts : HashMap? = null + /////////////////////////////////////////////////////////////////// // 以下はentityから取得したデータではなく、アプリ内部で使う @@ -223,7 +225,6 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() { // "mentionedRemoteUsers" -> "[{"uri":"https:\/\/mastodon.juggler.jp\/users\/tateisu","username":"tateisu","host":"mastodon.juggler.jp"}]" - this.tags = parseMisskeyTags(src.optJSONArray("tags")) this.application = parseItem(::TootApplication, parser, src.optJSONObject("app"), log) @@ -286,11 +287,12 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() { src.optJSONObject("poll") ) + this.reactionCounts = parseReactionCounts(src.optJSONObject("reactionCounts")) + this.reblog = parser.status(src.optJSONObject("renote")) } else { misskeyVisibleIds = null - this.uri = src.parseString("uri") // MSPだとuriは提供されない this.url = src.parseString("url") // 頻繁にnullになる this.created_at = src.parseString("created_at") @@ -437,20 +439,6 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() { } } - private fun parseMisskeyTags(src : JSONArray?) : ArrayList? { - var rv : ArrayList? = null - if(src != null) { - for(i in 0 until src.length()) { - val sv = src.optString(i, null) - if(sv?.isNotEmpty() == true) { - if(rv == null) rv = ArrayList() - rv.add(TootTag(name = sv)) - } - } - } - return rv - } - /////////////////////////////////////////////////// // ユーティリティ @@ -698,6 +686,33 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() { return rv } + private fun parseReactionCounts(src : JSONObject?) : HashMap? { + var rv : HashMap? = null + if(src != null) { + for(key in src.keys()) { + val v = src.parseInt(key) ?: continue + MisskeyReaction.shortcodeMap[key] ?: continue + if(rv == null) rv = HashMap() + rv[key] = v + } + } + return rv + } + + private fun parseMisskeyTags(src : JSONArray?) : ArrayList? { + var rv : ArrayList? = null + if(src != null) { + for(i in 0 until src.length()) { + val sv = src.optString(i, null) + if(sv?.isNotEmpty() == true) { + if(rv == null) rv = ArrayList() + rv.add(TootTag(name = sv)) + } + } + } + return rv + } + private fun validHost(host : String?) : String? { return if(host != null && host.isNotEmpty() && host != "?") host else null } diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/ActionsDialog.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/ActionsDialog.kt index 5a6aa818..793595a9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/ActionsDialog.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/ActionsDialog.kt @@ -21,7 +21,7 @@ class ActionsDialog { return this } - fun show(context : Context, title : CharSequence?) : ActionsDialog { + fun show(context : Context, title : CharSequence? = null ) : ActionsDialog { val caption_list = arrayOfNulls(action_list.size) var i = 0 val ie = caption_list.size diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e3c25bac..6285a3dc 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -748,6 +748,7 @@ Home Followers %1$d votes + Add reaction diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 642d667e..7525e009 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1026,6 +1026,7 @@ Misskey ホーム フォロワー - %1$d votes + %1$d票 + リアクションの追加 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b106f7f..0cb66546 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -734,4 +734,5 @@ Home Followers %1$d votes + Add reaction