アカウント情報を再利用してメモリ消費を軽減する

This commit is contained in:
tateisu 2018-05-08 17:25:02 +09:00
parent f1c602704c
commit 77bebca288
17 changed files with 667 additions and 477 deletions

View File

@ -328,7 +328,7 @@ class Column(
// プロフカラムでのアカウント情報
@Volatile
internal var who_account : TootAccount? = null
internal var who_account : TootAccountRef? = null
// リストカラムでのリスト情報
@Volatile
@ -612,13 +612,16 @@ class Column(
internal fun getColumnName(bLong : Boolean) : String {
return when(column_type) {
TYPE_PROFILE -> context.getString(
R.string.profile_of,
if(who_account != null)
AcctColor.getNickname(access_info.getFullAcct(who_account))
else
profile_id.toString()
)
TYPE_PROFILE -> {
val who = who_account?.find()
context.getString(
R.string.profile_of,
if(who != null)
AcctColor.getNickname(access_info.getFullAcct(who))
else
profile_id.toString()
)
}
TYPE_LIST_MEMBER -> context.getString(
R.string.list_member_of,
@ -748,8 +751,8 @@ class Column(
if(who_id == (o.account?.id ?: INVALID_ACCOUNT)) continue
if(who_id == (o.status?.account?.id ?: INVALID_ACCOUNT)) continue
if(who_id == (o.status?.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
} else if(o is TootAccount) {
if(who_id == o.id) continue
} else if(o is TootAccountRef) {
if(who_id == o.find().id) continue
}
tmp_list.add(o)
@ -767,8 +770,8 @@ class Column(
if(column_type == TYPE_MUTES && target_account.acct == access_info.acct) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for(o in list_data) {
if(o is TootAccount) {
if(o.id == who_id) continue
if(o is TootAccountRef) {
if(o.find().id == who_id) continue
}
tmp_list.add(o)
}
@ -785,8 +788,8 @@ class Column(
if(column_type == TYPE_BLOCKS && target_account.acct == access_info.acct) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for(o in list_data) {
if(o is TootAccount) {
if(o.id == who_id) continue
if(o is TootAccountRef) {
if(o.find().id == who_id) continue
}
tmp_list.add(o)
}
@ -805,8 +808,8 @@ class Column(
if(column_type == TYPE_FOLLOW_REQUESTS) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for(o in list_data) {
if(o is TootAccount) {
if(o.id == who_id) continue
if(o is TootAccountRef) {
if(o.find().id == who_id) continue
}
tmp_list.add(o)
}
@ -1259,7 +1262,8 @@ class Column(
internal fun loadProfileAccount(client : TootApiClient, bForceReload : Boolean) {
if(bForceReload || this.who_account == null) {
val result = client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id))
val a = TootParser(context, access_info).account(result?.jsonObject)
val parser =TootParser(context, access_info)
val a = TootAccountRef.mayNull(parser,parser.account(result?.jsonObject))
if(a != null) {
this.who_account = a
client.publishApiProgress("") // カラムヘッダの再表示
@ -1286,24 +1290,28 @@ class Column(
internal val acct_set = HashSet<String>()
internal val tag_set = HashSet<String>()
internal fun add(a : TootAccount?) {
if(a == null) return
who_set.add(a.id)
acct_set.add("@" + access_info.getFullAcct(a))
//
add(a.moved)
internal fun add(whoRef : TootAccountRef?) {
add(whoRef?.find())
}
internal fun add(who : TootAccount?) {
who?:return
who_set.add(who.id)
acct_set.add("@" + access_info.getFullAcct(who))
//
add(who.movedRef)
}
internal fun add(s : TootStatus?) {
if(s == null) return
add(s.account)
add(s.accountRef)
add(s.reblog)
s.tags?.forEach { tag_set.add(it.name) }
}
internal fun add(n : TootNotification?) {
if(n == null) return
add(n.account)
add(n.accountRef)
add(n.status)
}
@ -1382,17 +1390,17 @@ class Column(
private fun updateRelation(
client : TootApiClient,
list : ArrayList<TimelineItem>?,
who : TootAccount?
whoRef : TootAccountRef?
) {
if(access_info.isPseudo) return
val env = UpdateRelationEnv()
env.add(who)
env.add(whoRef)
list?.forEach {
when(it) {
is TootAccount -> env.add(it)
is TootAccountRef -> env.add(it)
is TootStatus -> env.add(it)
is TootNotification -> env.add(it)
}

View File

@ -20,7 +20,7 @@ import jp.juggler.subwaytooter.action.Action_Instance
import jp.juggler.subwaytooter.action.Action_Notification
import jp.juggler.subwaytooter.action.Action_Toot
import jp.juggler.subwaytooter.action.Action_User
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootNotification
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.dialog.DlgListMember
@ -35,7 +35,7 @@ import jp.juggler.subwaytooter.util.showToast
internal class DlgContextMenu(
val activity : ActMain,
private val column : Column,
private val who : TootAccount?,
private val whoRef : TootAccountRef?,
private val status : TootStatus?,
private val notification : TootNotification?
) : View.OnClickListener, View.OnLongClickListener {
@ -49,12 +49,12 @@ internal class DlgContextMenu(
private val dialog : Dialog
init {
this.access_info = column.access_info
val column_type = column.column_type
val who = this.who
val who = whoRef?.find()
val status = this.status
this.relation = UserRelation.load(access_info.db_id, who?.id ?: - 1)
@ -68,7 +68,8 @@ internal class DlgContextMenu(
val llStatus = viewRoot.findViewById<View>(R.id.llStatus)
val btnStatusWebPage = viewRoot.findViewById<View>(R.id.btnStatusWebPage)
val btnText = viewRoot.findViewById<View>(R.id.btnText)
val btnFavouriteAnotherAccount = viewRoot.findViewById<View>(R.id.btnFavouriteAnotherAccount)
val btnFavouriteAnotherAccount =
viewRoot.findViewById<View>(R.id.btnFavouriteAnotherAccount)
val btnBoostAnotherAccount = viewRoot.findViewById<View>(R.id.btnBoostAnotherAccount)
val btnReplyAnotherAccount = viewRoot.findViewById<View>(R.id.btnReplyAnotherAccount)
val btnDelete = viewRoot.findViewById<View>(R.id.btnDelete)
@ -84,14 +85,18 @@ internal class DlgContextMenu(
val btnAccountWebPage = viewRoot.findViewById<View>(R.id.btnAccountWebPage)
val btnFollowRequestOK = viewRoot.findViewById<View>(R.id.btnFollowRequestOK)
val btnFollowRequestNG = viewRoot.findViewById<View>(R.id.btnFollowRequestNG)
val btnFollowFromAnotherAccount = viewRoot.findViewById<View>(R.id.btnFollowFromAnotherAccount)
val btnSendMessageFromAnotherAccount = viewRoot.findViewById<View>(R.id.btnSendMessageFromAnotherAccount)
val btnOpenProfileFromAnotherAccount = viewRoot.findViewById<View>(R.id.btnOpenProfileFromAnotherAccount)
val btnFollowFromAnotherAccount =
viewRoot.findViewById<View>(R.id.btnFollowFromAnotherAccount)
val btnSendMessageFromAnotherAccount =
viewRoot.findViewById<View>(R.id.btnSendMessageFromAnotherAccount)
val btnOpenProfileFromAnotherAccount =
viewRoot.findViewById<View>(R.id.btnOpenProfileFromAnotherAccount)
val btnDomainBlock = viewRoot.findViewById<Button>(R.id.btnDomainBlock)
val btnInstanceInformation = viewRoot.findViewById<Button>(R.id.btnInstanceInformation)
val ivFollowedBy = viewRoot.findViewById<ImageView>(R.id.ivFollowedBy)
val btnOpenTimeline = viewRoot.findViewById<Button>(R.id.btnOpenTimeline)
val btnConversationAnotherAccount = viewRoot.findViewById<View>(R.id.btnConversationAnotherAccount)
val btnConversationAnotherAccount =
viewRoot.findViewById<View>(R.id.btnConversationAnotherAccount)
val btnAvatarImage = viewRoot.findViewById<View>(R.id.btnAvatarImage)
val llNotification = viewRoot.findViewById<View>(R.id.llNotification)
@ -162,13 +167,14 @@ internal class DlgContextMenu(
btnDelete.visibility = if(status_by_me) View.VISIBLE else View.GONE
btnReport.visibility = if(status_by_me || access_info.isPseudo) View.GONE else View.VISIBLE
btnReport.visibility =
if(status_by_me || access_info.isPseudo) View.GONE else View.VISIBLE
val application_name = status.application?.name
if(status_by_me || application_name ==null || application_name.isEmpty() ) {
if(status_by_me || application_name == null || application_name.isEmpty()) {
btnMuteApp.visibility = View.GONE
} else {
btnMuteApp.text = activity.getString(R.string.mute_app_of,application_name)
btnMuteApp.text = activity.getString(R.string.mute_app_of, application_name)
}
val btnBoostedBy = viewRoot.findViewById<View>(R.id.btnBoostedBy)
@ -190,7 +196,7 @@ internal class DlgContextMenu(
}
var bShowConversationMute = false
if(status != null) {
if( access_info.isMe(status.account) ){
if(access_info.isMe(status.account)) {
bShowConversationMute = true
} else if(notification != null && TootNotification.TYPE_MENTION == notification.type) {
bShowConversationMute = true
@ -200,7 +206,7 @@ internal class DlgContextMenu(
if(! bShowConversationMute) {
btnConversationMute.visibility = View.GONE
} else {
val muted = status ?. muted ?: false
val muted = status?.muted ?: false
btnConversationMute.setText(if(muted) R.string.unmute_this_conversation else R.string.mute_this_conversation)
}
@ -215,7 +221,12 @@ internal class DlgContextMenu(
ivFollowedBy.visibility = View.GONE
} else {
ivFollowedBy.visibility = View.VISIBLE
ivFollowedBy.setImageResource(Styler.getAttributeResourceId(activity, R.attr.ic_followed_by))
ivFollowedBy.setImageResource(
Styler.getAttributeResourceId(
activity,
R.attr.ic_followed_by
)
)
}
// follow button
@ -236,7 +247,8 @@ internal class DlgContextMenu(
// mute button
icon_attr = R.attr.ic_mute
color_attr = if(relation.muting) R.attr.colorImageButtonAccent else R.attr.colorImageButton
color_attr =
if(relation.muting) R.attr.colorImageButtonAccent else R.attr.colorImageButton
color = Styler.getAttributeColor(activity, color_attr)
d = Styler.getAttributeDrawable(activity, icon_attr).mutate()
d.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
@ -244,7 +256,8 @@ internal class DlgContextMenu(
// block button
icon_attr = R.attr.ic_block
color_attr = if(relation.blocking) R.attr.colorImageButtonAccent else R.attr.colorImageButton
color_attr =
if(relation.blocking) R.attr.colorImageButtonAccent else R.attr.colorImageButton
color = Styler.getAttributeColor(activity, color_attr)
d = Styler.getAttributeDrawable(activity, icon_attr).mutate()
d.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
@ -258,8 +271,9 @@ internal class DlgContextMenu(
} else {
val who_host = who.host
btnInstanceInformation.visibility = View.VISIBLE
btnInstanceInformation.text = activity.getString(R.string.instance_information_of, who_host)
if( access_info.isPseudo || access_info.host.equals( who_host,ignoreCase = true) ) {
btnInstanceInformation.text =
activity.getString(R.string.instance_information_of, who_host)
if(access_info.isPseudo || access_info.host.equals(who_host, ignoreCase = true)) {
// 疑似アカウントではドメインブロックできない
// 自ドメインはブロックできない
btnDomainBlock.visibility = View.GONE
@ -304,19 +318,23 @@ internal class DlgContextMenu(
btnShowBoost.visibility = View.VISIBLE
}
if( who == null ){
btnHideFavourite.visibility = View.GONE
btnShowFavourite.visibility = View.GONE
}else if( FavMute.contains( access_info.getFullAcct(who) )){
btnHideFavourite.visibility = View.GONE
btnShowFavourite.visibility = View.VISIBLE
}else{
btnHideFavourite.visibility = View.VISIBLE
btnShowFavourite.visibility = View.GONE
when {
who == null -> {
btnHideFavourite.visibility = View.GONE
btnShowFavourite.visibility = View.GONE
}
FavMute.contains(access_info.getFullAcct(who)) -> {
btnHideFavourite.visibility = View.GONE
btnShowFavourite.visibility = View.VISIBLE
}
else -> {
btnHideFavourite.visibility = View.VISIBLE
btnShowFavourite.visibility = View.GONE
}
}
val who_host = who?.host
if( who_host==null || who_host.isEmpty() || who_host == "?") {
if(who_host == null || who_host.isEmpty() || who_host == "?") {
btnOpenTimeline.visibility = View.GONE
} else {
btnOpenTimeline.text = activity.getString(R.string.open_local_timeline_for, who_host)
@ -327,7 +345,7 @@ internal class DlgContextMenu(
fun show() {
val window = dialog.window
if( window != null ){
if(window != null) {
val lp = window.attributes
lp.width = (0.5f + 280f * activity.density).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
@ -346,6 +364,207 @@ internal class DlgContextMenu(
val pos = activity.nextPosition(column)
val whoRef = this.whoRef
val who = whoRef?.find()
if(whoRef != null && who != null) {
when(v.id) {
R.id.btnReport -> if(status is TootStatus) {
Action_User.reportForm(activity, access_info, who, status)
}
R.id.btnFollow ->
if(access_info.isPseudo) {
Action_Follow.followFromAnotherAccount(activity, pos, access_info, who)
} else {
val bSet = ! (relation.getFollowing(who) || relation.getRequested(who))
Action_Follow.follow(
activity, pos, access_info, whoRef,
bFollow = bSet,
callback = if(bSet) activity.follow_complete_callback else activity.unfollow_complete_callback
)
}
R.id.btnAccountText ->
ActText.open(activity, ActMain.REQUEST_CODE_TEXT, access_info, who)
R.id.btnMute ->
if(relation.muting) {
//解除
Action_User.mute(
activity,
access_info,
who,
bMute = false
)
} else {
@SuppressLint("InflateParams")
val view =
activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
tvMessage.text =
activity.getString(R.string.confirm_mute_user, who.username)
val cbMuteNotification = view.findViewById<CheckBox>(R.id.cbSkipNext)
cbMuteNotification.setText(R.string.confirm_mute_notification_for_user)
cbMuteNotification.isChecked = true
// オプション指定つきでミュート
AlertDialog.Builder(activity)
.setView(view)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
Action_User.mute(
activity,
access_info,
who,
bMuteNotification = cbMuteNotification.isChecked
)
}
.show()
}
R.id.btnBlock ->
if(relation.blocking) {
Action_User.block(activity, access_info, who, false)
} else {
AlertDialog.Builder(activity)
.setMessage(
activity.getString(
R.string.confirm_block_user,
who.username
)
)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
Action_User.block(
activity,
access_info,
who,
true
)
}
.show()
}
R.id.btnProfile ->
Action_User.profileLocal(activity, pos, access_info, who)
R.id.btnSendMessage ->
Action_User.mention(activity, access_info, who)
R.id.btnAccountWebPage -> who.url?.let { url ->
App1.openCustomTab(activity, url)
}
R.id.btnFollowRequestOK ->
Action_Follow.authorizeFollowRequest(activity, access_info, whoRef, true)
R.id.btnFollowRequestNG ->
Action_Follow.authorizeFollowRequest(activity, access_info, whoRef, false)
R.id.btnFollowFromAnotherAccount ->
Action_Follow.followFromAnotherAccount(activity, pos, access_info, who)
R.id.btnSendMessageFromAnotherAccount ->
Action_User.mentionFromAnotherAccount(activity, access_info, who)
R.id.btnOpenProfileFromAnotherAccount ->
Action_User.profileFromAnotherAccount(activity, pos, access_info, who)
R.id.btnNickname ->
ActNickname.open(
activity,
access_info.getFullAcct(who),
true,
ActMain.REQUEST_CODE_NICKNAME
)
R.id.btnAccountQrCode ->
DlgQRCode.open(
activity,
whoRef.decoded_display_name,
access_info.getUserUrl(who.acct)
)
R.id.btnDomainBlock ->
if(access_info.isPseudo) {
// 疑似アカウントではドメインブロックできない
showToast(activity, false, R.string.domain_block_from_pseudo)
return
} else {
val who_host = who.host
// 自分のドメインではブロックできない
if(access_info.host.equals(who_host, ignoreCase = true)) {
showToast(activity, false, R.string.domain_block_from_local)
return
}
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.confirm_block_domain, who_host))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
Action_Instance.blockDomain(activity, access_info, who_host, true)
}
.show()
}
R.id.btnOpenTimeline -> {
val who_host = who.host
if(who_host.isEmpty() || who_host == "?") {
// 何もしない
} else {
Action_Instance.timelineLocal(activity, pos, who_host)
}
}
R.id.btnAvatarImage -> {
val url = if(! who.avatar.isNullOrEmpty()) who.avatar else who.avatar_static
if(url != null && url.isNotEmpty()) App1.openCustomTab(activity, url)
// XXX: 設定によっては内蔵メディアビューアで開けないか?
}
R.id.btnQuoteName -> {
var sv = who.display_name
try {
val fmt = Pref.spQuoteNameFormat(activity.pref)
if(fmt.contains("%1\$s")) {
sv = String.format(fmt, sv)
}
} catch(ex : Throwable) {
log.trace(ex)
}
Action_Account.openPost(activity, sv)
}
R.id.btnHideBoost ->
Action_User.showBoosts(activity, access_info, who, false)
R.id.btnShowBoost ->
Action_User.showBoosts(activity, access_info, who, true)
R.id.btnHideFavourite -> {
val acct = access_info.getFullAcct(who)
FavMute.save(acct)
showToast(activity, false, R.string.changed)
for(column in activity.app_state.column_list) {
column.onHideFavouriteNotification(acct)
}
}
R.id.btnShowFavourite -> {
FavMute.delete(access_info.getFullAcct(who))
showToast(activity, false, R.string.changed)
}
R.id.btnListMemberAddRemove ->
DlgListMember(activity, who, access_info).show()
R.id.btnInstanceInformation ->
Action_Instance.information(activity, pos, who.host)
}
}
when(v.id) {
R.id.btnStatusWebPage -> status?.url?.let { url ->
@ -356,184 +575,56 @@ internal class DlgContextMenu(
ActText.open(activity, ActMain.REQUEST_CODE_TEXT, access_info, status)
}
R.id.btnFavouriteAnotherAccount -> Action_Toot.favouriteFromAnotherAccount(activity, access_info, status)
R.id.btnFavouriteAnotherAccount -> Action_Toot.favouriteFromAnotherAccount(
activity,
access_info,
status
)
R.id.btnBoostAnotherAccount -> Action_Toot.boostFromAnotherAccount(activity, access_info, status)
R.id.btnBoostAnotherAccount -> Action_Toot.boostFromAnotherAccount(
activity,
access_info,
status
)
R.id.btnReplyAnotherAccount -> Action_Toot.replyFromAnotherAccount(activity, access_info, status)
R.id.btnReplyAnotherAccount -> Action_Toot.replyFromAnotherAccount(
activity,
access_info,
status
)
R.id.btnConversationAnotherAccount -> status?.let { status->
R.id.btnConversationAnotherAccount -> status?.let { status ->
Action_Toot.conversationOtherInstance(activity, pos, status)
}
R.id.btnDelete -> status?.let { status->
R.id.btnDelete -> status?.let { status ->
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.confirm_delete_status))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> Action_Toot.delete(activity, access_info, status.id) }
.setPositiveButton(R.string.ok) { _, _ ->
Action_Toot.delete(
activity,
access_info,
status.id
)
}
.show()
}
R.id.btnReport -> who?.let{ who->
if( status is TootStatus) {
Action_User.reportForm(activity, access_info, who, status)
}
}
R.id.btnMuteApp -> status?.application?.let {
Action_App.muteApp(activity, it)
}
R.id.btnBoostedBy -> status?.let {
activity.addColumn(false,pos, access_info, Column.TYPE_BOOSTED_BY, it.id)
activity.addColumn(false, pos, access_info, Column.TYPE_BOOSTED_BY, it.id)
}
R.id.btnFavouritedBy -> status?.let {
activity.addColumn(false,pos, access_info, Column.TYPE_FAVOURITED_BY, it.id)
}
R.id.btnFollow -> who?.let { who ->
if(access_info.isPseudo) {
Action_Follow.followFromAnotherAccount(activity, pos, access_info, who)
} else {
val bSet = ! (relation.getFollowing(who) || relation.getRequested(who))
Action_Follow.follow(
activity, pos, access_info, who,
bFollow= bSet,
callback = if(bSet) activity.follow_complete_callback else activity.unfollow_complete_callback
)
}
}
R.id.btnAccountText -> who?.let { who ->
ActText.open(activity, ActMain.REQUEST_CODE_TEXT, access_info, who)
}
R.id.btnMute -> who?.let { who ->
if(relation.muting) {
//解除
Action_User.mute(
activity,
access_info,
who,
bMute=false
)
} else {
@SuppressLint("InflateParams")
val view = activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
tvMessage.text = activity.getString(R.string.confirm_mute_user, who.username)
val cbMuteNotification = view.findViewById<CheckBox>(R.id.cbSkipNext)
cbMuteNotification.setText(R.string.confirm_mute_notification_for_user)
cbMuteNotification.isChecked = true
// オプション指定つきでミュート
AlertDialog.Builder(activity)
.setView(view)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
Action_User.mute(
activity,
access_info,
who,
bMuteNotification=cbMuteNotification.isChecked
)
}
.show()
}
}
R.id.btnBlock -> who?.let { who ->
// サーバのバグで誰のことか分からないので何もできない
if(relation.blocking) {
Action_User.block(activity, access_info, who, false)
} else {
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.confirm_block_user, who.username))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> Action_User.block(activity, access_info, who, true) }
.show()
}
}
R.id.btnProfile -> who?.let { who ->
Action_User.profileLocal(activity, pos, access_info, who)
}
R.id.btnSendMessage -> who?.let { who ->
Action_User.mention(activity, access_info, who)
}
R.id.btnAccountWebPage -> who?.url?.let { url ->
App1.openCustomTab(activity, url)
}
R.id.btnFollowRequestOK -> who?.let { who ->
Action_Follow.authorizeFollowRequest(activity, access_info, who, true)
}
R.id.btnFollowRequestNG -> who?.let { who ->
Action_Follow.authorizeFollowRequest(activity, access_info, who, false)
}
R.id.btnFollowFromAnotherAccount -> who?.let { who ->
Action_Follow.followFromAnotherAccount(activity, pos, access_info, who)
}
R.id.btnSendMessageFromAnotherAccount -> who?.let { who ->
Action_User.mentionFromAnotherAccount(activity, access_info, who)
}
R.id.btnOpenProfileFromAnotherAccount -> who?.let { who ->
Action_User.profileFromAnotherAccount(activity, pos, access_info, who)
}
R.id.btnNickname -> who?.let { who ->
ActNickname.open(activity, access_info.getFullAcct(who), true, ActMain.REQUEST_CODE_NICKNAME)
activity.addColumn(false, pos, access_info, Column.TYPE_FAVOURITED_BY, it.id)
}
R.id.btnCancel -> dialog.cancel()
R.id.btnAccountQrCode -> who?.let { who ->
DlgQRCode.open(activity, who.decoded_display_name , access_info.getUserUrl(who.acct))
}
R.id.btnDomainBlock -> who?.let { who ->
// 疑似アカウントではドメインブロックできない
if(access_info.isPseudo) {
showToast(activity, false, R.string.domain_block_from_pseudo)
return@let
}
val who_host = who.host
// 自分のドメインではブロックできない
if( access_info.host.equals(who_host,ignoreCase = true)) {
showToast(activity, false, R.string.domain_block_from_local)
return@let
}
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.confirm_block_domain, who_host))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
Action_Instance.blockDomain(activity, access_info, who_host, true)
}
.show()
}
R.id.btnOpenTimeline -> {
val who_host = who?.host
if( who_host?.isEmpty() != false || who_host == "?" ) {
// 何もしない
} else {
Action_Instance.timelineLocal(activity, pos,who_host)
}
}
R.id.btnAvatarImage -> who?.let { who ->
val url = if(!who.avatar.isNullOrEmpty() ) who.avatar else who.avatar_static
if( url != null && url.isNotEmpty() ) App1.openCustomTab(activity, url)
// XXX: 設定によっては内蔵メディアビューアで開けないか?
}
R.id.btnQuoteUrlStatus -> status?.url?.let { url ->
if(url.isNotEmpty()) Action_Account.openPost(activity, url)
}
@ -542,7 +633,7 @@ internal class DlgContextMenu(
if(url.isNotEmpty()) Action_Account.openPost(activity, url)
}
R.id.btnNotificationDelete -> notification?.let{ notification->
R.id.btnNotificationDelete -> notification?.let { notification ->
Action_Notification.deleteOne(activity, access_info, notification)
}
@ -550,24 +641,6 @@ internal class DlgContextMenu(
Action_Toot.muteConversation(activity, access_info, status)
}
R.id.btnQuoteName -> who?.let { who ->
var sv = who.display_name
try {
val fmt = Pref.spQuoteNameFormat( activity.pref)
if( fmt.contains("%1\$s")) {
sv = String.format(fmt, sv)
}
} catch(ex : Throwable) {
log.trace(ex)
}
Action_Account.openPost(activity, sv)
}
R.id.btnInstanceInformation -> who?.let { who ->
Action_Instance.information(activity, pos, who.host)
}
R.id.btnProfilePin -> status?.let { status ->
Action_Toot.pin(activity, access_info, status, true)
}
@ -576,35 +649,11 @@ internal class DlgContextMenu(
Action_Toot.pin(activity, access_info, status, false)
}
R.id.btnHideBoost -> who?.let { who ->
Action_User.showBoosts(activity, access_info, who, false)
}
R.id.btnShowBoost -> who?.let { who ->
Action_User.showBoosts(activity, access_info, who, true)
}
R.id.btnHideFavourite -> who?.let { who ->
val acct = access_info.getFullAcct(who)
FavMute.save( acct)
showToast(activity,false,R.string.changed)
for( column in activity.app_state.column_list ){
column.onHideFavouriteNotification(acct)
}
}
R.id.btnShowFavourite -> who?.let { who ->
FavMute.delete( access_info.getFullAcct(who))
showToast(activity,false,R.string.changed)
}
R.id.btnListMemberAddRemove -> who?.let { who ->
DlgListMember(activity, who, access_info).show()
}
}
}
override fun onLongClick(v : View) : Boolean {
val who = whoRef?.find()
when(v.id) {
R.id.btnFollow -> {
@ -613,13 +662,16 @@ internal class DlgContextMenu(
} catch(ignored : Throwable) {
// IllegalArgumentException がたまに出る
}
Action_Follow.followFromAnotherAccount(activity, activity.nextPosition(column), access_info, who)
Action_Follow.followFromAnotherAccount(
activity,
activity.nextPosition(column),
access_info,
who
)
return true
}
}
return false
}
}

View File

@ -126,9 +126,9 @@ internal class ItemViewHolder(
private var item : TimelineItem? = null
private var status_showing : TootStatus? = null
private var status_account : TootAccount? = null
private var boost_account : TootAccount? = null
private var follow_account : TootAccount? = null
private var status_account : TootAccountRef? = null
private var boost_account : TootAccountRef? = null
private var follow_account : TootAccountRef? = null
private var boost_time : Long = 0L
@ -344,7 +344,7 @@ internal class ItemViewHolder(
this.item = item
when(item) {
is TootTag -> showSearchTag(item)
is TootAccount -> showAccount(item)
is TootAccountRef -> showAccount(item)
is TootNotification -> showNotification(item)
is TootGap -> showGap()
is TootDomainBlock -> showDomainBlock(item)
@ -354,10 +354,10 @@ internal class ItemViewHolder(
val reblog = item.reblog
if(reblog != null) {
showBoost(
item.account,
item.accountRef,
item.time_created_at,
R.attr.btn_boost,
item.account.decoded_display_name.intoStringResource(
item.accountRef.decoded_display_name.intoStringResource(
activity,
R.string.display_name_boosted_by
)
@ -375,14 +375,15 @@ internal class ItemViewHolder(
private fun showNotification(n : TootNotification) {
val n_status = n.status
val n_account = n.account
val n_accountRef = n.accountRef
val n_account = n_accountRef?.find()
when(n.type) {
TootNotification.TYPE_FAVOURITE -> {
if(n_account != null) showBoost(
n_account,
n_accountRef,
n.time_created_at,
if(access_info.isNicoru(n_account)) R.attr.ic_nicoru else R.attr.btn_favourite,
n_account.decoded_display_name.intoStringResource(
n_accountRef.decoded_display_name.intoStringResource(
activity,
R.string.display_name_favourited_by
)
@ -392,10 +393,10 @@ internal class ItemViewHolder(
TootNotification.TYPE_REBLOG -> {
if(n_account != null) showBoost(
n_account,
n_accountRef,
n.time_created_at,
R.attr.btn_boost,
n_account.decoded_display_name.intoStringResource(
n_accountRef.decoded_display_name.intoStringResource(
activity,
R.string.display_name_boosted_by
)
@ -407,25 +408,25 @@ internal class ItemViewHolder(
TootNotification.TYPE_FOLLOW -> {
if(n_account != null) {
showBoost(
n_account,
n_accountRef,
n.time_created_at,
R.attr.ic_follow_plus,
n_account.decoded_display_name.intoStringResource(
n_accountRef.decoded_display_name.intoStringResource(
activity,
R.string.display_name_followed_by
)
)
showAccount(n_account)
showAccount(n_accountRef)
}
}
TootNotification.TYPE_MENTION -> {
if(! bSimpleList) {
if(n_account != null) showBoost(
n_account,
n_accountRef,
n.time_created_at,
R.attr.btn_reply,
n_account.decoded_display_name.intoStringResource(
n_accountRef.decoded_display_name.intoStringResource(
activity,
R.string.display_name_replied_by
)
@ -460,8 +461,9 @@ internal class ItemViewHolder(
btnSearchTag.text = activity.getString(R.string.read_gap)
}
private fun showBoost(who : TootAccount, time : Long, icon_attr_id : Int, text : Spannable) {
boost_account = who
private fun showBoost(whoRef : TootAccountRef, time : Long, icon_attr_id : Int, text : Spannable) {
boost_account = whoRef
val who = whoRef.find()
boost_time = time
llBoosted.visibility = View.VISIBLE
ivBoosted.setImageResource(Styler.getAttributeResourceId(activity, icon_attr_id))
@ -471,16 +473,17 @@ internal class ItemViewHolder(
setAcct(tvBoostedAcct, access_info.getFullAcct(who), who.acct)
}
private fun showAccount(who : TootAccount) {
follow_account = who
private fun showAccount(whoRef : TootAccountRef) {
follow_account = whoRef
val who = whoRef.find()
llFollow.visibility = View.VISIBLE
ivFollow.setImageUrl(
activity.pref,
Styler.calcIconRound(ivFollow.layoutParams),
access_info.supplyBaseUrl(who.avatar_static)
)
tvFollowerName.text = who.decoded_display_name
follow_invalidator.register(who.decoded_display_name)
tvFollowerName.text = whoRef.decoded_display_name
follow_invalidator.register(whoRef.decoded_display_name)
setAcct(tvFollowerAcct, access_info.getFullAcct(who), who.acct)
@ -498,8 +501,9 @@ internal class ItemViewHolder(
showStatusTime(activity, tvTime, who = status.account, status = status)
val who = status.account
this.status_account = who
val whoRef = status.accountRef
val who = whoRef.find()
this.status_account = whoRef
setAcct(tvAcct, access_info.getFullAcct(who), who.acct)
@ -508,8 +512,8 @@ internal class ItemViewHolder(
// name_invalidator.register(null)
// ivThumbnail.setImageUrl(activity.pref, 16f, null, null)
// } else {
tvName.text = who.decoded_display_name
name_invalidator.register(who.decoded_display_name)
tvName.text = whoRef.decoded_display_name
name_invalidator.register(whoRef.decoded_display_name)
ivThumbnail.setImageUrl(
activity.pref,
Styler.calcIconRound(ivThumbnail.layoutParams),
@ -874,27 +878,27 @@ internal class ItemViewHolder(
}
ivThumbnail -> status_account?.let { who ->
ivThumbnail -> status_account?.let { whoRef ->
if(access_info.isPseudo) {
DlgContextMenu(activity, column, who, null, notification).show()
DlgContextMenu(activity, column, whoRef, null, notification).show()
} else {
Action_User.profileLocal(activity, pos, access_info, who)
Action_User.profileLocal(activity, pos, access_info, whoRef.find())
}
}
llBoosted -> boost_account?.let { who ->
llBoosted -> boost_account?.let { whoRef ->
if(access_info.isPseudo) {
DlgContextMenu(activity, column, who, null, notification).show()
DlgContextMenu(activity, column, whoRef, null, notification).show()
} else {
Action_User.profileLocal(activity, pos, access_info, who)
Action_User.profileLocal(activity, pos, access_info, whoRef.find())
}
}
llFollow -> follow_account?.let { who ->
llFollow -> follow_account?.let { whoRef ->
if(access_info.isPseudo) {
DlgContextMenu(activity, column, who, null, notification).show()
DlgContextMenu(activity, column, whoRef, null, notification).show()
} else {
Action_User.profileLocal(activity, pos, access_info, who)
Action_User.profileLocal(activity, pos, access_info, whoRef.find())
}
}
btnFollow -> follow_account?.let { who ->
@ -962,15 +966,17 @@ internal class ItemViewHolder(
.show(activity, item.title)
}
btnFollowRequestAccept -> follow_account?.let { who ->
btnFollowRequestAccept -> follow_account?.let { whoRef ->
val who = whoRef.find()
DlgConfirm.openSimple(activity,activity.getString(R.string.follow_accept_confirm,AcctColor.getNickname(access_info.getFullAcct(who)))){
Action_Follow.authorizeFollowRequest(activity, access_info, who, true)
Action_Follow.authorizeFollowRequest(activity, access_info, whoRef, true)
}
}
btnFollowRequestDeny -> follow_account?.let { who ->
btnFollowRequestDeny -> follow_account?.let { whoRef ->
val who = whoRef.find()
DlgConfirm.openSimple(activity,activity.getString(R.string.follow_deny_confirm,AcctColor.getNickname(access_info.getFullAcct(who)))){
Action_Follow.authorizeFollowRequest(activity, access_info, who, false)
Action_Follow.authorizeFollowRequest(activity, access_info, whoRef, false)
}
}
@ -1016,11 +1022,11 @@ internal class ItemViewHolder(
}
llFollow -> {
follow_account?.let { who ->
follow_account?.let { whoRef ->
DlgContextMenu(
activity,
column,
who,
whoRef,
null,
notification
).show()
@ -1029,12 +1035,12 @@ internal class ItemViewHolder(
}
btnFollow -> {
follow_account?.let { who ->
follow_account?.let { whoRef ->
Action_Follow.followFromAnotherAccount(
activity,
activity.nextPosition(column),
access_info,
who
whoRef.find()
)
}
return true

View File

@ -1579,7 +1579,7 @@ class PollingWorker private constructor(c : Context) {
var a = getNotificationLine(
item.notification.type,
item.notification.account?.decoded_display_name ?: "?"
item.notification.accountRef?.decoded_display_name ?: "?"
)
val acct = item.access_info.acct
if(data_list.size == 1) {
@ -1598,7 +1598,7 @@ class PollingWorker private constructor(c : Context) {
item = data_list[i]
a = getNotificationLine(
item.notification.type,
item.notification.account?.decoded_display_name ?: "?"
item.notification.accountRef?.decoded_display_name ?: "?"
)
style.addLine(a)
}

View File

@ -226,7 +226,8 @@ internal class StatusButtons(
}
btnFollow2 -> {
val account = status.account
val accountRef = status.accountRef
val account = accountRef.find()
val relation = this.relation ?: return
when {
@ -250,7 +251,7 @@ internal class StatusButtons(
activity,
activity.nextPosition(column),
access_info,
account,
accountRef,
bFollow = false,
callback = activity.unfollow_complete_callback
)
@ -262,7 +263,7 @@ internal class StatusButtons(
activity,
activity.nextPosition(column),
access_info,
account,
accountRef,
bFollow = true,
callback = activity.follow_complete_callback
)
@ -270,7 +271,7 @@ internal class StatusButtons(
}
}
btnMore -> DlgContextMenu(activity, column, status.account, status, notification).show()
btnMore -> DlgContextMenu(activity, column, status.accountRef, status, notification).show()
}
}

View File

@ -15,6 +15,7 @@ import jp.juggler.emoji.EmojiMap201709
import jp.juggler.subwaytooter.action.Action_Follow
import jp.juggler.subwaytooter.action.Action_User
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.UserRelation
@ -49,9 +50,9 @@ internal class ViewHolderHeaderProfile(
private val note_invalidator : NetworkEmojiInvalidator
private val llFields : LinearLayout
private var who : TootAccount? = null
private var whoRef : TootAccountRef? = null
private var who_moved : TootAccount? = null
private var movedRef : TootAccountRef? = null
private val llMoved : View
private val tvMoved : TextView
@ -140,8 +141,9 @@ internal class ViewHolderHeaderProfile(
tvCreated.textSize = activity.acct_font_size_sp
}
val who = column.who_account
this.who = who
val whoRef = column.who_account
this.whoRef = whoRef
val who = whoRef?.find()
showColor()
@ -184,7 +186,7 @@ internal class ViewHolderHeaderProfile(
access_info.supplyBaseUrl(who.avatar)
)
val name = who.decoded_display_name
val name = whoRef.decoded_display_name
tvDisplayName.text = name
name_invalidator.register(name)
@ -210,7 +212,7 @@ internal class ViewHolderHeaderProfile(
}
tvAcct.text = sb
val note = who.decoded_note
val note = whoRef.decoded_note
tvNote.text = note
note_invalidator.register(note)
@ -221,7 +223,7 @@ internal class ViewHolderHeaderProfile(
val relation = UserRelation.load(access_info.db_id, who.id)
Styler.setFollowIcon(activity, btnFollow, ivFollowedBy, relation, who)
showMoved(who, who.moved)
showMoved(who, who.movedRef)
if(who.fields != null) {
@ -295,9 +297,10 @@ internal class ViewHolderHeaderProfile(
}
}
private fun showMoved(who : TootAccount, who_moved : TootAccount?) {
if(who_moved == null) return
this.who_moved = who_moved
private fun showMoved(who : TootAccount, movedRef : TootAccountRef?) {
if(movedRef == null) return
this.movedRef = movedRef
val moved = movedRef.find()
llMoved.visibility = View.VISIBLE
tvMoved.visibility = View.VISIBLE
@ -314,16 +317,16 @@ internal class ViewHolderHeaderProfile(
ivMoved.setImageUrl(
activity.pref,
Styler.calcIconRound(ivMoved.layoutParams),
access_info.supplyBaseUrl(who_moved.avatar_static)
access_info.supplyBaseUrl(moved.avatar_static)
)
tvMovedName.text = who_moved.decoded_display_name
moved_name_invalidator.register(who_moved.decoded_display_name)
tvMovedName.text = movedRef.decoded_display_name
moved_name_invalidator.register(movedRef.decoded_display_name)
setAcct(tvMovedAcct, access_info.getFullAcct(who_moved), who_moved.acct)
setAcct(tvMovedAcct, access_info.getFullAcct(moved), moved.acct)
val relation = UserRelation.load(access_info.db_id, who_moved.id)
Styler.setFollowIcon(activity, btnMoved, ivMovedBy, relation, who_moved)
val relation = UserRelation.load(access_info.db_id, moved.id)
Styler.setFollowIcon(activity, btnMoved, ivMovedBy, relation, moved)
}
private fun setAcct(tv : TextView, acctLong : String, acctShort : String) {
@ -353,7 +356,7 @@ internal class ViewHolderHeaderProfile(
when(v.id) {
R.id.ivBackground, R.id.tvRemoteProfileWarning -> who?.url?.let { url ->
R.id.ivBackground, R.id.tvRemoteProfileWarning -> whoRef?.find()?.url?.let { url ->
App1.openCustomTab(activity, url)
}
@ -375,27 +378,27 @@ internal class ViewHolderHeaderProfile(
column.startLoading()
}
R.id.btnMore -> who?.let { who ->
DlgContextMenu(activity, column, who, null, null).show()
R.id.btnMore -> whoRef?.let { whoRef ->
DlgContextMenu(activity, column, whoRef, null, null).show()
}
R.id.btnFollow -> who?.let { who ->
DlgContextMenu(activity, column, who, null, null).show()
R.id.btnFollow -> whoRef?.let { whoRef ->
DlgContextMenu(activity, column, whoRef, null, null).show()
}
R.id.btnMoved -> who_moved?.let { who_moved ->
DlgContextMenu(activity, column, who_moved, null, null).show()
R.id.btnMoved -> movedRef?.let { movedRef ->
DlgContextMenu(activity, column, movedRef, null, null).show()
}
R.id.llMoved -> who_moved?.let { who_moved ->
R.id.llMoved -> movedRef?.let { movedRef ->
if(access_info.isPseudo) {
DlgContextMenu(activity, column, who_moved, null, null).show()
DlgContextMenu(activity, column, movedRef, null, null).show()
} else {
Action_User.profileLocal(
activity,
activity.nextPosition(column),
access_info,
who_moved
movedRef.find()
)
}
}
@ -410,7 +413,7 @@ internal class ViewHolderHeaderProfile(
activity,
activity.nextPosition(column),
access_info,
who
whoRef?.find()
)
return true
}
@ -420,7 +423,7 @@ internal class ViewHolderHeaderProfile(
activity,
activity.nextPosition(column),
access_info,
who_moved
movedRef?.find()
)
return true
}
@ -433,7 +436,7 @@ internal class ViewHolderHeaderProfile(
}
fun updateRelativeTime() {
val who = this.who
val who = whoRef?.find()
if(who != null) {
tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
}

View File

@ -7,6 +7,7 @@ import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootRelationShip
import jp.juggler.subwaytooter.api.entity.parseItem
import jp.juggler.subwaytooter.dialog.AccountPicker
@ -26,12 +27,14 @@ object Action_Follow {
activity : ActMain,
pos : Int,
access_info : SavedAccount,
who : TootAccount,
bFollow : Boolean =true,
bConfirmMoved : Boolean =false,
bConfirmed : Boolean =false,
whoRef : TootAccountRef,
bFollow : Boolean = true,
bConfirmMoved : Boolean = false,
bConfirmed : Boolean = false,
callback : EmptyCallback? = null
) {
val who = whoRef.find()
if(access_info.isMe(who)) {
showToast(activity, false, R.string.it_is_you)
return
@ -39,15 +42,27 @@ object Action_Follow {
if(! bConfirmMoved && bFollow && who.moved != null) {
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.jump_moved_user, access_info.getFullAcct(who), access_info.getFullAcct(who.moved)
))
.setPositiveButton(R.string.ok) { _, _ -> Action_User.profileFromAnotherAccount(activity, pos, access_info, who.moved) }
.setMessage(
activity.getString(
R.string.jump_moved_user,
access_info.getFullAcct(who),
access_info.getFullAcct(who.moved)
)
)
.setPositiveButton(R.string.ok) { _, _ ->
Action_User.profileFromAnotherAccount(
activity,
pos,
access_info,
who.moved
)
}
.setNeutralButton(R.string.ignore_suggestion) { _, _ ->
follow(
activity,
pos,
access_info,
who,
whoRef,
bFollow = bFollow,
bConfirmMoved = true, // CHANGED
bConfirmed = bConfirmed,
@ -63,33 +78,41 @@ object Action_Follow {
if(bFollow && who.locked) {
DlgConfirm.open(
activity,
activity.getString(R.string.confirm_follow_request_who_from, who.decoded_display_name, AcctColor.getNickname(access_info.acct))
activity.getString(
R.string.confirm_follow_request_who_from,
whoRef.decoded_display_name,
AcctColor.getNickname(access_info.acct)
)
, object : DlgConfirm.Callback {
override fun onOK() {
follow(
activity,
pos,
access_info,
who,
bFollow = bFollow,
bConfirmMoved =bConfirmMoved,
bConfirmed=true, // CHANGED
callback=callback
)
}
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow_locked
set(value) {
access_info.confirm_follow_locked = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
override fun onOK() {
follow(
activity,
pos,
access_info,
whoRef,
bFollow = bFollow,
bConfirmMoved = bConfirmMoved,
bConfirmed = true, // CHANGED
callback = callback
)
}
})
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow_locked
set(value) {
access_info.confirm_follow_locked = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
}
})
return
} else if(bFollow) {
val msg = activity.getString(R.string.confirm_follow_who_from, who.decoded_display_name, AcctColor.getNickname(access_info.acct))
val msg = activity.getString(
R.string.confirm_follow_who_from,
whoRef.decoded_display_name,
AcctColor.getNickname(access_info.acct)
)
DlgConfirm.open(
activity,
@ -101,11 +124,12 @@ object Action_Follow {
activity,
pos,
access_info,
who,
bFollow=bFollow,
bConfirmMoved=bConfirmMoved,
bConfirmed =true, //CHANGED
callback=callback)
whoRef,
bFollow = bFollow,
bConfirmMoved = bConfirmMoved,
bConfirmed = true, //CHANGED
callback = callback
)
}
override var isConfirmEnabled : Boolean
@ -120,7 +144,11 @@ object Action_Follow {
} else {
DlgConfirm.open(
activity,
activity.getString(R.string.confirm_unfollow_who_from, who.decoded_display_name, AcctColor.getNickname(access_info.acct)),
activity.getString(
R.string.confirm_unfollow_who_from,
whoRef.decoded_display_name,
AcctColor.getNickname(access_info.acct)
),
object : DlgConfirm.Callback {
override fun onOK() {
@ -128,11 +156,12 @@ object Action_Follow {
activity,
pos,
access_info,
who,
bFollow=bFollow,
bConfirmMoved=bConfirmMoved,
bConfirmed =true, // CHANGED
callback=callback)
whoRef,
bFollow = bFollow,
bConfirmMoved = bConfirmMoved,
bConfirmed = true, // CHANGED
callback = callback
)
}
override var isConfirmEnabled : Boolean
@ -158,11 +187,13 @@ object Action_Follow {
// リモートフォローする
val request_builder = Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + who.acct.encodePercent()
))
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
"uri=" + who.acct.encodePercent()
)
)
result = client.request("/api/v1/follows", request_builder)
val remote_who = TootParser(activity, access_info).account( result?.jsonObject)
val remote_who = TootParser(activity, access_info).account(result?.jsonObject)
if(remote_who != null) {
val rr = loadRelation1(client, access_info, remote_who.id)
result = rr.result
@ -176,12 +207,16 @@ object Action_Follow {
val request_builder = Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "" // 空データ
))
result = client.request("/api/v1/accounts/" + who.id
+ if(bFollow) "/follow" else "/unfollow", request_builder)
)
)
result = client.request(
"/api/v1/accounts/" + who.id
+ if(bFollow) "/follow" else "/unfollow", request_builder
)
val jsonObject = result?.jsonObject
if(jsonObject != null) {
relation = saveUserRelation(access_info, parseItem(::TootRelationShip,jsonObject))
relation =
saveUserRelation(access_info, parseItem(::TootRelationShip, jsonObject))
}
}
@ -197,17 +232,17 @@ object Action_Follow {
activity.showColumnMatchAccount(access_info)
if(bFollow && relation .getRequested(who)) {
if(bFollow && relation.getRequested(who)) {
// 鍵付きアカウントにフォローリクエストを申請した状態
showToast(activity, false, R.string.follow_requested)
} else if(! bFollow && relation .getRequested(who)) {
} else if(! bFollow && relation.getRequested(who)) {
showToast(activity, false, R.string.follow_request_cant_remove_by_sender)
} else {
// ローカル操作成功、もしくはリモートフォロー成功
if(callback != null) callback()
}
} else if(bFollow && who.locked && ( result.response?.code() ?: -1) == 422) {
} else if(bFollow && who.locked && (result.response?.code() ?: - 1) == 422) {
showToast(activity, false, R.string.cant_follow_locked_user)
} else {
showToast(activity, false, result.error)
@ -223,8 +258,8 @@ object Action_Follow {
access_info : SavedAccount,
acct : String,
locked : Boolean,
bConfirmed : Boolean =false,
callback : EmptyCallback? =null
bConfirmed : Boolean = false,
callback : EmptyCallback? = null
) {
if(access_info.isMe(acct)) {
showToast(activity, false, R.string.it_is_you)
@ -233,49 +268,63 @@ object Action_Follow {
if(! bConfirmed) {
if(locked) {
DlgConfirm.open(activity, activity.getString(R.string.confirm_follow_request_who_from, AcctColor.getNickname(acct), AcctColor.getNickname(access_info.acct)), object : DlgConfirm.Callback {
override fun onOK() {
followRemote(
activity,
access_info,
acct,
locked,
bConfirmed= true, //CHANGE
callback=callback
)
}
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow_locked
set(value) {
access_info.confirm_follow_locked = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
DlgConfirm.open(
activity,
activity.getString(
R.string.confirm_follow_request_who_from,
AcctColor.getNickname(acct),
AcctColor.getNickname(access_info.acct)
),
object : DlgConfirm.Callback {
override fun onOK() {
followRemote(
activity,
access_info,
acct,
locked,
bConfirmed = true, //CHANGE
callback = callback
)
}
})
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow_locked
set(value) {
access_info.confirm_follow_locked = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
}
})
return
} else {
DlgConfirm.open(activity, activity.getString(R.string.confirm_follow_who_from, AcctColor.getNickname(acct), AcctColor.getNickname(access_info.acct)), object : DlgConfirm.Callback {
override fun onOK() {
followRemote(
activity,
access_info,
acct,
locked,
bConfirmed= true, //CHANGE
callback=callback
)
}
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow
set(value) {
access_info.confirm_follow = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
DlgConfirm.open(
activity,
activity.getString(
R.string.confirm_follow_who_from,
AcctColor.getNickname(acct),
AcctColor.getNickname(access_info.acct)
),
object : DlgConfirm.Callback {
override fun onOK() {
followRemote(
activity,
access_info,
acct,
locked,
bConfirmed = true, //CHANGE
callback = callback
)
}
})
override var isConfirmEnabled : Boolean
get() = access_info.confirm_follow
set(value) {
access_info.confirm_follow = value
access_info.saveSetting()
activity.reloadAccountSetting(access_info)
}
})
return
}
}
@ -289,13 +338,14 @@ object Action_Follow {
val request_builder = Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + acct.encodePercent()
))
)
)
var result = client.request("/api/v1/follows", request_builder)
val remote_who = TootParser(activity, access_info).account(result?.jsonObject)
if(remote_who != null) {
this.remote_who = remote_who
val rr = loadRelation1(client, access_info, remote_who .id)
val rr = loadRelation1(client, access_info, remote_who.id)
result = rr.result
relation = rr.relation
}
@ -313,7 +363,7 @@ object Action_Follow {
if(callback != null) callback()
} else if(locked && (result.response?.code() ?: -1 )== 422) {
} else if(locked && (result.response?.code() ?: - 1) == 422) {
showToast(activity, false, R.string.cant_follow_locked_user)
} else {
showToast(activity, false, result.error)
@ -328,14 +378,19 @@ object Action_Follow {
pos : Int,
access_info : SavedAccount,
account : TootAccount?,
bConfirmMoved : Boolean =false
bConfirmMoved : Boolean = false
) {
if(account == null) return
if(! bConfirmMoved && account.moved != null) {
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.jump_moved_user, access_info.getFullAcct(account), access_info.getFullAcct(account.moved)
))
.setMessage(
activity.getString(
R.string.jump_moved_user,
access_info.getFullAcct(account),
access_info.getFullAcct(account.moved)
)
)
.setPositiveButton(R.string.ok) { _, _ ->
Action_User.profileFromAnotherAccount(activity, pos, access_info, account.moved)
}
@ -371,8 +426,12 @@ object Action_Follow {
}
fun authorizeFollowRequest(
activity : ActMain, access_info : SavedAccount, who : TootAccount, bAllow : Boolean
activity : ActMain,
access_info : SavedAccount,
whoRef : TootAccountRef,
bAllow : Boolean
) {
val who = whoRef.find()
if(access_info.isMe(who)) {
showToast(activity, false, R.string.it_is_you)
return
@ -383,10 +442,13 @@ object Action_Follow {
val request_builder = Request.Builder().post(
RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "" // 空データ
))
)
)
return client.request(
"/api/v1/follow_requests/" + who.id + if(bAllow) "/authorize" else "/reject", request_builder)
"/api/v1/follow_requests/" + who.id + if(bAllow) "/authorize" else "/reject",
request_builder
)
}
override fun handleResult(result : TootApiResult?) {
@ -398,7 +460,12 @@ object Action_Follow {
column.removeFollowRequest(access_info, who.id)
}
showToast(activity, false, if(bAllow) R.string.follow_request_authorized else R.string.follow_request_rejected, who.decoded_display_name)
showToast(
activity,
false,
if(bAllow) R.string.follow_request_authorized else R.string.follow_request_rejected,
whoRef.decoded_display_name
)
} else {
showToast(activity, false, result.error)
}

View File

@ -226,7 +226,7 @@ object Action_User {
if(jsonObject != null) {
val tmp = TootParser(activity, access_info).results(jsonObject)
if(tmp != null && tmp.accounts.isNotEmpty()) {
who_local = tmp.accounts[0]
who_local = tmp.accounts[0].find()
} else {
return TootApiResult(activity.getString(R.string.user_id_conversion_failed))
}

View File

@ -56,9 +56,10 @@ class DuplicateMap {
}
is TootAccount -> {
if(set_account_id.contains(o.id)) return true
set_account_id.add(o.id)
is TootAccountRef -> {
val id = o.find().id
if(set_account_id.contains(id)) return true
set_account_id.add(id)
}
}

View File

@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.api
import jp.juggler.subwaytooter.api.entity.ServiceType
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import java.util.concurrent.ConcurrentHashMap
object TootAccountMap{
@ -48,6 +49,9 @@ object TootAccountMap{
fun find(id : Int) : TootAccount {
return requireNotNull(accountMap[id])
}
fun find(ref: TootAccountRef) : TootAccount {
return requireNotNull(accountMap[ref.id])
}
fun register(parser:TootParser, who : TootAccount) : Int {
val key = AccountUniqueKey(parser,who)

View File

@ -18,7 +18,7 @@ class TootParser(
) {
fun account(src : JSONObject?) = parseItem(::TootAccount, this, src)
fun accountList(array : JSONArray?) = parseList(::TootAccount, this, array)
fun accountList(array : JSONArray?) = TootAccountRef.wrapList(this,parseList(::TootAccount, this, array))
fun status(src : JSONObject?) = parseItem(::TootStatus, this, src)
fun statusList(array : JSONArray?) = parseList(::TootStatus, this, array)

View File

@ -15,7 +15,7 @@ import java.util.regex.Pattern
open class TootAccount(
parser : TootParser,
src : JSONObject
) : TimelineItem() {
) {
//URL of the user's profile page (can be remote)
// https://mastodon.juggler.jp/@tateisu
@ -36,9 +36,6 @@ open class TootAccount(
// The account's display name
val display_name : String
// The account's display name
val decoded_display_name : Spannable
//Boolean for when the account cannot be followed without waiting for approval first
val locked : Boolean
@ -60,8 +57,6 @@ open class TootAccount(
// 説明文。改行は\r\n。リンクなどはHTMLタグで書かれている
val note : String?
val decoded_note : Spannable
// URL to the avatar image
val avatar : String?
@ -78,19 +73,22 @@ open class TootAccount(
val profile_emojis : HashMap<String, NicoProfileEmoji>?
val movedRef : TootAccountRef?
val moved : TootAccount?
get() = movedRef?.find()
val fields : ArrayList<Pair<String, String>>?
val custom_emojis : java.util.HashMap<String, CustomEmoji>?
val bot :Boolean
val bot : Boolean
init {
var sv : String?
// 絵文字データは先に読んでおく
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis") )
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"))
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"))
// 疑似アカウントにacctとusernameだけ
@ -100,28 +98,22 @@ open class TootAccount(
//
sv = src.parseString("display_name")
this.display_name = if(sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
this.decoded_display_name = decodeDisplayName(parser.context)
//
this.note = src.parseString("note")
this.decoded_note = DecodeOptions(
parser.context,
parser.linkHelper,
short = true,
decodeEmoji = true,
emojiMapProfile = this.profile_emojis,
emojiMapCustom = this.custom_emojis,
unwrapEmojiImageTag = true
).decodeHTML(this.note)
this.source = parseSource(src.optJSONObject("source"))
this.moved =
src.optJSONObject("moved")?.let { TootAccount(parser, it) }
this.movedRef = TootAccountRef.mayNull(
parser,
src.optJSONObject("moved")?.let {
TootAccount(parser, it)
}
)
this.locked = src.optBoolean("locked")
this.fields = parseFields(src.optJSONArray("fields"))
this.bot = src.optBoolean("bot",false)
this.bot = src.optBoolean("bot", false)
when(parser.serviceType) {
ServiceType.MASTODON -> {
@ -297,9 +289,9 @@ open class TootAccount(
val v = item.parseString("value") ?: continue
dst.add(Pair(k, v))
}
return if( dst.isEmpty() ){
return if(dst.isEmpty()) {
null
}else{
} else {
dst
}
}

View File

@ -0,0 +1,49 @@
package jp.juggler.subwaytooter.api.entity
import android.text.Spannable
import jp.juggler.subwaytooter.api.TootAccountMap
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.util.DecodeOptions
class TootAccountRef(parser: TootParser, account:TootAccount) : TimelineItem() {
val id:Int
// The account's display name
val decoded_display_name : Spannable
val decoded_note : Spannable
init{
this.id = TootAccountMap.register(parser,account)
this.decoded_display_name = account.decodeDisplayName(parser.context)
this.decoded_note = DecodeOptions(
parser.context,
parser.linkHelper,
short = true,
decodeEmoji = true,
emojiMapProfile = account.profile_emojis,
emojiMapCustom = account.custom_emojis,
unwrapEmojiImageTag = true
).decodeHTML(account.note)
}
fun find() : TootAccount {
return TootAccountMap.find(this)
}
companion object {
fun mayNull(parser: TootParser, account:TootAccount?) :TootAccountRef? {
return when(account) {
null -> null
else -> TootAccountRef(parser,account)
}
}
fun wrapList(parser: TootParser, src : ArrayList<TootAccount>) : ArrayList<TootAccountRef> {
val dst = ArrayList<TootAccountRef>()
for( a in src){
dst.add( TootAccountRef(parser,a))
}
return dst
}
}
}

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter.api.entity
import jp.juggler.subwaytooter.api.TootAccountMap
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.util.notEmptyOrThrow
import jp.juggler.subwaytooter.util.parseLong
@ -12,11 +13,14 @@ class TootNotification(
val id : Long,
val type : String, // One of: "mention", "reblog", "favourite", "follow"
private val created_at : String?, // The time the notification was created
val account : TootAccount?, // The Account sending the notification to the user
val accountRef : TootAccountRef?, // The Account sending the notification to the user
val status : TootStatus? // The Status associated with the notification, if applicable
) : TimelineItem() {
val time_created_at : Long
val account : TootAccount?
get() = accountRef?.find()
init {
time_created_at = TootStatus.parseTime(created_at)
@ -27,7 +31,7 @@ class TootNotification(
id = src.parseLong("id") ?: - 1L,
type = src.notEmptyOrThrow("type"),
created_at = src.parseString("created_at"),
account = parser.account(src.optJSONObject("account")),
accountRef = TootAccountRef.mayNull( parser, parser.account(src.optJSONObject("account"))),
status = parser.status(src.optJSONObject("status"))
)

View File

@ -7,7 +7,7 @@ import java.util.ArrayList
import jp.juggler.subwaytooter.api.TootParser
class TootResults(
val accounts : ArrayList<TootAccount>, // An array of matched Accounts
val accounts : ArrayList<TootAccountRef>, // An array of matched Accounts
val statuses : ArrayList<TootStatus>, // An array of matched Statuses
val hashtags : ArrayList<TootTag> // An array of matched hashtags
) {

View File

@ -47,12 +47,14 @@ class TootStatus(parser : TootParser, src : JSONObject) :
// 取得に失敗するとINVALID_IDになる
val id : Long
// The TootAccount which posted the status
val accountRef : Int
val accountRef : TootAccountRef
val account : TootAccount
get(){
return TootAccountMap.find(accountRef)
return TootAccountMap.find(accountRef.id)
}
//The number of reblogs for the status
@ -164,7 +166,7 @@ class TootStatus(parser : TootParser, src : JSONObject) :
val who =parser.account(src.optJSONObject("account"))
?: throw RuntimeException("missing account")
this.accountRef = TootAccountMap.register(parser,who)
this.accountRef = TootAccountRef( parser,who)
when(parser.serviceType) {
ServiceType.MASTODON -> {

View File

@ -192,7 +192,8 @@ class DlgListMember(
val search_result = TootParser(activity, list_owner ).results(jsonObject)
if(search_result != null) {
for(a in search_result.accounts) {
for(aRef in search_result.accounts) {
val a = aRef.find()
if(target_user_full_acct.equals(list_owner .getFullAcct(a), ignoreCase = true)) {
local_who = a
break