diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActHighlightWordList.kt b/app/src/main/java/jp/juggler/subwaytooter/ActHighlightWordList.kt index afcad9ab..1e5e2dc3 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActHighlightWordList.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActHighlightWordList.kt @@ -250,7 +250,7 @@ class ActHighlightWordList : AppCompatActivity(), View.OnClickListener { } private fun create() { - DlgTextInput.show(this, getString(R.string.new_item), "", object : DlgTextInput.Callback { + DlgTextInput.show(this, getString(R.string.new_item), "", callback = object : DlgTextInput.Callback { override fun onEmptyError() { showToast(this@ActHighlightWordList, true, R.string.word_empty) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt index 2030f313..09bc49cc 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt @@ -2178,7 +2178,7 @@ class ActMain : AsyncActivity() this, getString(R.string.access_token_or_api_token), null, - object : DlgTextInput.Callback { + callback = object : DlgTextInput.Callback { override fun onOK(dialog : Dialog, text : String) { checkAccessToken(null, dialog, sa.host, text, sa) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt index e2ed47ba..64909fab 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt @@ -1757,7 +1757,7 @@ class ActPost : AsyncActivity(), this, getString(R.string.attachment_description), a.description, - object : DlgTextInput.Callback { + callback = object : DlgTextInput.Callback { override fun onOK(dialog : Dialog, text : String) { setAttachmentDescription(pa, dialog, text) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/App1.kt b/app/src/main/java/jp/juggler/subwaytooter/App1.kt index 8c385e88..e58809f6 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/App1.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/App1.kt @@ -134,8 +134,9 @@ class App1 : Application() { // 2019/12/18 44=> 45 SavedAccount テーブルに項目追加。 // 2019/12/18 44=> 46 SavedAccount テーブルに項目追加。 // 2020/6/8 46 => 54 別ブランチで色々してた。このブランチには影響ないが onDowngrade()を実装してないので上げてしまう + // 2020/7/19 54=>55 UserRelation テーブルに項目追加。 - internal const val DB_VERSION = 54 + internal const val DB_VERSION = 55 private val tableList = arrayOf( LogData, diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.kt b/app/src/main/java/jp/juggler/subwaytooter/Column.kt index 2c823a5a..c8ada552 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.kt @@ -1706,35 +1706,31 @@ class Column( // リロード不要なら何もしない null } else if(isMisskey) { - val result = client.request( + client.request( PATH_MISSKEY_PROFILE, access_info.putMisskeyApiToken().apply { put("userId", profile_id) }.toPostRequestBuilder() - ) - - // ユーザリレーションの取り扱いのため、別のparserを作ってはいけない - parser.misskeyDecodeProfilePin = true - try { - val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject)) - if(a != null) { + )?.also{ result1 -> + // ユーザリレーションの取り扱いのため、別のparserを作ってはいけない + parser.misskeyDecodeProfilePin = true + try { + TootAccountRef.mayNull(parser, parser.account(result1.jsonObject))?.also{ a-> + this.who_account = a + client.publishApiProgress("") // カラムヘッダの再表示 + } + } finally { + parser.misskeyDecodeProfilePin = false + } + } + } else { + client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id))?.also{result1 -> + TootAccountRef.mayNull(parser, parser.account(result1.jsonObject))?.also{ a-> this.who_account = a client.publishApiProgress("") // カラムヘッダの再表示 + } - } finally { - parser.misskeyDecodeProfilePin = false } - - result - - } else { - val result = client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id)) - val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject)) - if(a != null) { - this.who_account = a - client.publishApiProgress("") // カラムヘッダの再表示 - } - result } } @@ -1781,7 +1777,8 @@ class Column( // parser内部にアカウントIDとRelationのマップが生成されるので、それをデータベースに記録する run { val now = System.currentTimeMillis() - val who_list = parser.misskeyUserRelationMap.entries.toMutableList() + val who_list = + parser.misskeyUserRelationMap.entries.toMutableList() var start = 0 val end = who_list.size while(start < end) { diff --git a/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt b/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt index 37792055..b30457a1 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt @@ -1,5 +1,6 @@ package jp.juggler.subwaytooter +import android.app.Dialog import android.graphics.Color import android.text.SpannableStringBuilder import android.text.Spanned @@ -9,10 +10,11 @@ import android.widget.* import jp.juggler.emoji.EmojiMap import jp.juggler.subwaytooter.action.Action_Follow import jp.juggler.subwaytooter.action.Action_User -import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap +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.TootStatus +import jp.juggler.subwaytooter.dialog.DlgTextInput import jp.juggler.subwaytooter.span.EmojiImageSpan import jp.juggler.subwaytooter.span.createSpan import jp.juggler.subwaytooter.table.AcctColor @@ -68,6 +70,9 @@ internal class ViewHolderHeaderProfile( private val density : Float private val btnMore : ImageButton + private val tvPersonalNotes : TextView + private val btnPersonalNotesEdit : ImageButton + init { ivBackground = viewRoot.findViewById(R.id.ivBackground) llProfile = viewRoot.findViewById(R.id.llProfile) @@ -95,18 +100,28 @@ internal class ViewHolderHeaderProfile( ivMovedBy = viewRoot.findViewById(R.id.ivMovedBy) llFields = viewRoot.findViewById(R.id.llFields) + tvPersonalNotes = viewRoot.findViewById(R.id.tvPersonalNotes) + btnPersonalNotesEdit = viewRoot.findViewById(R.id.btnPersonalNotesEdit) + + density = tvDisplayName.resources.displayMetrics.density - ivBackground.setOnClickListener(this) - btnFollowing.setOnClickListener(this) - btnFollowers.setOnClickListener(this) - btnStatusCount.setOnClickListener(this) - btnMore.setOnClickListener(this) - btnFollow.setOnClickListener(this) - tvRemoteProfileWarning.setOnClickListener(this) - - btnMoved.setOnClickListener(this) - llMoved.setOnClickListener(this) + for(v in arrayOf( + ivBackground, + btnFollowing, + btnFollowers, + btnStatusCount, + btnMore, + btnFollow, + tvRemoteProfileWarning, + btnPersonalNotesEdit, + + btnMoved, + llMoved, + btnPersonalNotesEdit + )) { + v.setOnClickListener(this) + } btnMoved.setOnLongClickListener(this) btnFollow.setOnLongClickListener(this) @@ -132,6 +147,8 @@ internal class ViewHolderHeaderProfile( private var contentColor = 0 + private var relation : UserRelation? = null + override fun bindData(column : Column) { super.bindData(column) @@ -141,6 +158,7 @@ internal class ViewHolderHeaderProfile( if(! f.isNaN()) { tvMovedName.textSize = f tvMoved.textSize = f + tvPersonalNotes.textSize = f } f = activity.acct_font_size_sp @@ -159,6 +177,7 @@ internal class ViewHolderHeaderProfile( val contentColor = column.getContentColor() this.contentColor = contentColor + tvPersonalNotes.textColor = contentColor tvMoved.textColor = contentColor tvMovedName.textColor = contentColor tvDisplayName.textColor = contentColor @@ -175,7 +194,15 @@ internal class ViewHolderHeaderProfile( color = contentColor, alphaMultiplier = Styler.boost_alpha ) - + + setIconDrawableId( + activity, + btnPersonalNotesEdit, + R.drawable.ic_edit, + color = contentColor, + alphaMultiplier = Styler.boost_alpha + ) + val acctColor = column.getAcctColor() tvCreated.textColor = acctColor tvMovedAcct.textColor = acctColor @@ -200,6 +227,7 @@ internal class ViewHolderHeaderProfile( llFields.removeAllViews() if(who == null) { + relation = null tvCreated.text = "" tvLastStatusAt.vg(false) ivBackground.setImageDrawable(null) @@ -345,6 +373,8 @@ internal class ViewHolderHeaderProfile( } val relation = UserRelation.load(access_info.db_id, who.id) + this.relation = relation + Styler.setFollowIcon( activity, btnFollow, @@ -355,6 +385,8 @@ internal class ViewHolderHeaderProfile( alphaMultiplier = Styler.boost_alpha ) + tvPersonalNotes.text = relation.note ?: "" + showMoved(who, who.movedRef) val fields = whoDetail?.fields ?: who.fields @@ -550,6 +582,53 @@ internal class ViewHolderHeaderProfile( ) } } + + R.id.btnPersonalNotesEdit -> whoRef?.let{ whoRef-> + val who = whoRef.get() + val relation = this.relation + val lastColumn = column + DlgTextInput.show( + activity, + AcctColor.getStringWithNickname(activity,R.string.personal_notes_of,who.acct), + relation?.note ?: "", + allowEmpty = true, + callback = object: DlgTextInput.Callback{ + override fun onEmptyError() { + } + + override fun onOK(dialog : Dialog, text : String) { + TootTaskRunner(activity).run(column.access_info,object:TootTask{ + override fun background(client : TootApiClient) : TootApiResult? { + + if(access_info.isPseudo) + return TootApiResult("Personal notes is not supported on pseudo account.") + + if(access_info.isMisskey) + return TootApiResult("Personal notes is not supported on Misskey account.") + + return client.request("/api/v1/accounts/${who.id}/note", + jsonObject { + put("comment",text) + }.toPostRequestBuilder() + ) + } + + override fun handleResult(result : TootApiResult?) { + if(result==null) return + if(result.error!=null) + showToast(activity,true,result.error) + else{ + relation?.note = text + dialog.dismissSafe() + if(lastColumn==column) bindData(column) + } + } + }) + } + } + ) + + } } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt index 14d4105a..09e58de4 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt @@ -93,7 +93,7 @@ object Action_Account { activity, activity.getString(R.string.access_token_or_api_token), null, - object : DlgTextInput.Callback { + callback = object : DlgTextInput.Callback { @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") override fun onOK( diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/Action_List.kt b/app/src/main/java/jp/juggler/subwaytooter/action/Action_List.kt index 8c2ee0c1..7821e67e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/Action_List.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/Action_List.kt @@ -140,7 +140,7 @@ object Action_List { activity, activity.getString(R.string.rename), item.title, - object : DlgTextInput.Callback { + callback = object : DlgTextInput.Callback { override fun onEmptyError() { showToast(activity, false, R.string.list_name_empty) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootRelationShip.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootRelationShip.kt index e95af7dd..fc2c32dd 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootRelationShip.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootRelationShip.kt @@ -39,6 +39,9 @@ class TootRelationShip(parser:TootParser,src : JsonObject) { // misskey用 val requested_by : Boolean + + // (Mastodon 3.2) + var note : String? = null init { @@ -91,6 +94,7 @@ class TootRelationShip(parser:TootParser,src : JsonObject) { this.muting = src.optBoolean("muting") this.requested = src.optBoolean("requested") this.endorsed = src.optBoolean("endorsed") + this.note = src.optString( "note") // https://github.com/tootsuite/mastodon/commit/9745de883b198375ba23f7fde879f6d75ce2df0f // Mastodon 2.8.0から diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgListMember.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgListMember.kt index 8cadd101..1f8ed44a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgListMember.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgListMember.kt @@ -265,7 +265,7 @@ class DlgListMember( activity, activity.getString(R.string.list_create), null, - object : DlgTextInput.Callback { + callback = object : DlgTextInput.Callback { override fun onEmptyError() { showToast(activity, false, R.string.list_name_empty) diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgTextInput.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgTextInput.kt index b0d65dcb..d61bd1b8 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgTextInput.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgTextInput.kt @@ -24,6 +24,7 @@ object DlgTextInput { activity : Activity, caption : CharSequence, initial_text : CharSequence?, + allowEmpty : Boolean = false, callback : Callback ) { val view = activity.layoutInflater.inflate(R.layout.dlg_text_input, null, false) @@ -51,7 +52,7 @@ object DlgTextInput { btnOk.setOnClickListener { val token = etInput.text.toString().trim { it <= ' ' } - if( token.isEmpty() ) { + if( token.isEmpty() && !allowEmpty ) { callback.onEmptyError() } else { callback.onOK(dialog, token) diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/TableUtils.kt b/app/src/main/java/jp/juggler/subwaytooter/table/TableUtils.kt index 17d0626f..67376aee 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/TableUtils.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/table/TableUtils.kt @@ -1,5 +1,6 @@ package jp.juggler.subwaytooter.table +import android.content.ContentValues import android.database.Cursor import android.database.sqlite.SQLiteDatabase @@ -21,3 +22,6 @@ interface TableCompanion { fun onDBCreate(db : SQLiteDatabase) fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int) } + +fun ContentValues.putOrNull(key : String, value : String?) = + if(value == null) putNull(key) else put(key, value) diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/UserRelation.kt b/app/src/main/java/jp/juggler/subwaytooter/table/UserRelation.kt index a6bbba47..255a963a 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/table/UserRelation.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/table/UserRelation.kt @@ -13,6 +13,7 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip import jp.juggler.util.JsonObject import jp.juggler.util.LogCategory import jp.juggler.util.getInt +import jp.juggler.util.getStringOrNull class UserRelation { @@ -26,6 +27,8 @@ class UserRelation { var following_reblogs : Int = 0 // このユーザからのブーストをTLに表示する var endorsed : Boolean = false // ユーザをプロフィールで紹介する + var note : String? = null + // 認証ユーザからのフォロー状態 fun getFollowing(who : TootAccount?) : Boolean { return if(requested && ! following && who != null && ! who.locked) true else following @@ -62,6 +65,7 @@ class UserRelation { private const val COL_ENDORSED = "endorsed" private const val COL_BLOCKED_BY = "blocked_by" private const val COL_REQUESTED_BY = "requested_by" + private const val COL_NOTE = "note" private const val DB_ID_PSEUDO = - 2L @@ -83,6 +87,7 @@ class UserRelation { ,$COL_ENDORSED integer default 0 ,$COL_BLOCKED_BY integer default 0 ,$COL_REQUESTED_BY integer default 0 + ,$COL_NOTE text default null )""" ) db.execSQL( @@ -119,6 +124,13 @@ class UserRelation { log.trace(ex) } } + if(oldVersion < 55 && newVersion >= 55) { + try { + db.execSQL("alter table $table add column $COL_NOTE text default null") + } catch(ex : Throwable) { + log.trace(ex) + } + } } fun deleteOld(now : Long) { @@ -155,6 +167,7 @@ class UserRelation { cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) + cv.putOrNull(COL_NOTE, src.note) App1.database.replaceOrThrow(table, null, cv) val key = String.format("%s:%s", db_id, whoId) @@ -184,6 +197,7 @@ class UserRelation { cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) + cv.putOrNull(COL_NOTE, src.note) App1.database.replaceOrThrow(table, null, cv) val key = String.format("%s:%s", db_id, id) mMemoryCache.remove(key) @@ -216,6 +230,7 @@ class UserRelation { cv.put(COL_REQUESTED, src.requested.b2i()) cv.put(COL_FOLLOWING_REBLOGS, src.showing_reblogs) cv.put(COL_ENDORSED, src.endorsed.b2i()) + cv.putOrNull(COL_NOTE, src.note) db.replaceOrThrow(table, null, cv) } @@ -267,6 +282,7 @@ class UserRelation { cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) + cv.putOrNull(COL_NOTE, src.note) db.replaceOrThrow(table, null, cv) } bOK = true @@ -307,6 +323,7 @@ class UserRelation { cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) + cv.putOrNull(COL_NOTE, src.note) db.replace(table, null, cv) } bOK = true @@ -365,6 +382,7 @@ class UserRelation { dst.endorsed = cursor.getBoolean(COL_ENDORSED) dst.blocked_by = cursor.getBoolean(COL_BLOCKED_BY) dst.requested_by = cursor.getBoolean(COL_REQUESTED_BY) + dst.note = cursor.getStringOrNull(COL_NOTE) return dst } } diff --git a/app/src/main/res/layout/lv_header_profile.xml b/app/src/main/res/layout/lv_header_profile.xml index 26ac16d2..9f05cf12 100644 --- a/app/src/main/res/layout/lv_header_profile.xml +++ b/app/src/main/res/layout/lv_header_profile.xml @@ -220,6 +220,39 @@ tools:text="xxxx-xx-xx xx:xx:xx" /> + + + + + + + 認証済みリンクの背景色 (アプリ再起動が必要) 認証済みリンクの文字色 (アプリ再起動が必要) 画面下部の余白(単位:dp。デフォルト:0。アプリ再起動が必要) + 覚え書き + %1$sに関する覚え書き diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a054c6d6..f052ebe8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1035,4 +1035,6 @@ Verified link background color (app restart required) Verified link foreground color (app restart required) Screen bottom padding (Unit: dp. Default:0. App restart required) + Personal notes + Personal notes of %1$s