(Mastodon 3.2)プロフカラムで覚え書き(Personal notes)を記録/表示できる。覚書は記録した本人だけが閲覧できる。

This commit is contained in:
tateisu 2020-07-19 21:28:12 +09:00
parent 1f64df766e
commit bab7bfdfd8
16 changed files with 183 additions and 42 deletions

View File

@ -250,7 +250,7 @@ class ActHighlightWordList : AppCompatActivity(), View.OnClickListener {
} }
private fun create() { 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() { override fun onEmptyError() {
showToast(this@ActHighlightWordList, true, R.string.word_empty) showToast(this@ActHighlightWordList, true, R.string.word_empty)
} }

View File

@ -2178,7 +2178,7 @@ class ActMain : AsyncActivity()
this, this,
getString(R.string.access_token_or_api_token), getString(R.string.access_token_or_api_token),
null, null,
object : DlgTextInput.Callback { callback = object : DlgTextInput.Callback {
override fun onOK(dialog : Dialog, text : String) { override fun onOK(dialog : Dialog, text : String) {
checkAccessToken(null, dialog, sa.host, text, sa) checkAccessToken(null, dialog, sa.host, text, sa)
} }

View File

@ -1757,7 +1757,7 @@ class ActPost : AsyncActivity(),
this, this,
getString(R.string.attachment_description), getString(R.string.attachment_description),
a.description, a.description,
object : DlgTextInput.Callback { callback = object : DlgTextInput.Callback {
override fun onOK(dialog : Dialog, text : String) { override fun onOK(dialog : Dialog, text : String) {
setAttachmentDescription(pa, dialog, text) setAttachmentDescription(pa, dialog, text)
} }

View File

@ -134,8 +134,9 @@ class App1 : Application() {
// 2019/12/18 44=> 45 SavedAccount テーブルに項目追加。 // 2019/12/18 44=> 45 SavedAccount テーブルに項目追加。
// 2019/12/18 44=> 46 SavedAccount テーブルに項目追加。 // 2019/12/18 44=> 46 SavedAccount テーブルに項目追加。
// 2020/6/8 46 => 54 別ブランチで色々してた。このブランチには影響ないが onDowngrade()を実装してないので上げてしまう // 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( private val tableList = arrayOf(
LogData, LogData,

View File

@ -1706,35 +1706,31 @@ class Column(
// リロード不要なら何もしない // リロード不要なら何もしない
null null
} else if(isMisskey) { } else if(isMisskey) {
val result = client.request( client.request(
PATH_MISSKEY_PROFILE, PATH_MISSKEY_PROFILE,
access_info.putMisskeyApiToken().apply { access_info.putMisskeyApiToken().apply {
put("userId", profile_id) put("userId", profile_id)
}.toPostRequestBuilder() }.toPostRequestBuilder()
) )?.also{ result1 ->
// ユーザリレーションの取り扱いのため、別のparserを作ってはいけない
// ユーザリレーションの取り扱いのため、別のparserを作ってはいけない parser.misskeyDecodeProfilePin = true
parser.misskeyDecodeProfilePin = true try {
try { TootAccountRef.mayNull(parser, parser.account(result1.jsonObject))?.also{ a->
val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject)) this.who_account = a
if(a != null) { 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 this.who_account = a
client.publishApiProgress("") // カラムヘッダの再表示 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のマップが生成されるので、それをデータベースに記録する // parser内部にアカウントIDとRelationのマップが生成されるので、それをデータベースに記録する
run { run {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val who_list = parser.misskeyUserRelationMap.entries.toMutableList() val who_list =
parser.misskeyUserRelationMap.entries.toMutableList()
var start = 0 var start = 0
val end = who_list.size val end = who_list.size
while(start < end) { while(start < end) {

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter package jp.juggler.subwaytooter
import android.app.Dialog
import android.graphics.Color import android.graphics.Color
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.Spanned import android.text.Spanned
@ -9,10 +10,11 @@ import android.widget.*
import jp.juggler.emoji.EmojiMap import jp.juggler.emoji.EmojiMap
import jp.juggler.subwaytooter.action.Action_Follow import jp.juggler.subwaytooter.action.Action_Follow
import jp.juggler.subwaytooter.action.Action_User 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.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootStatus import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.dialog.DlgTextInput
import jp.juggler.subwaytooter.span.EmojiImageSpan import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.createSpan import jp.juggler.subwaytooter.span.createSpan
import jp.juggler.subwaytooter.table.AcctColor import jp.juggler.subwaytooter.table.AcctColor
@ -68,6 +70,9 @@ internal class ViewHolderHeaderProfile(
private val density : Float private val density : Float
private val btnMore : ImageButton private val btnMore : ImageButton
private val tvPersonalNotes : TextView
private val btnPersonalNotesEdit : ImageButton
init { init {
ivBackground = viewRoot.findViewById(R.id.ivBackground) ivBackground = viewRoot.findViewById(R.id.ivBackground)
llProfile = viewRoot.findViewById(R.id.llProfile) llProfile = viewRoot.findViewById(R.id.llProfile)
@ -95,18 +100,28 @@ internal class ViewHolderHeaderProfile(
ivMovedBy = viewRoot.findViewById(R.id.ivMovedBy) ivMovedBy = viewRoot.findViewById(R.id.ivMovedBy)
llFields = viewRoot.findViewById(R.id.llFields) llFields = viewRoot.findViewById(R.id.llFields)
tvPersonalNotes = viewRoot.findViewById(R.id.tvPersonalNotes)
btnPersonalNotesEdit = viewRoot.findViewById(R.id.btnPersonalNotesEdit)
density = tvDisplayName.resources.displayMetrics.density density = tvDisplayName.resources.displayMetrics.density
ivBackground.setOnClickListener(this) for(v in arrayOf(
btnFollowing.setOnClickListener(this) ivBackground,
btnFollowers.setOnClickListener(this) btnFollowing,
btnStatusCount.setOnClickListener(this) btnFollowers,
btnMore.setOnClickListener(this) btnStatusCount,
btnFollow.setOnClickListener(this) btnMore,
tvRemoteProfileWarning.setOnClickListener(this) btnFollow,
tvRemoteProfileWarning,
btnMoved.setOnClickListener(this) btnPersonalNotesEdit,
llMoved.setOnClickListener(this)
btnMoved,
llMoved,
btnPersonalNotesEdit
)) {
v.setOnClickListener(this)
}
btnMoved.setOnLongClickListener(this) btnMoved.setOnLongClickListener(this)
btnFollow.setOnLongClickListener(this) btnFollow.setOnLongClickListener(this)
@ -132,6 +147,8 @@ internal class ViewHolderHeaderProfile(
private var contentColor = 0 private var contentColor = 0
private var relation : UserRelation? = null
override fun bindData(column : Column) { override fun bindData(column : Column) {
super.bindData(column) super.bindData(column)
@ -141,6 +158,7 @@ internal class ViewHolderHeaderProfile(
if(! f.isNaN()) { if(! f.isNaN()) {
tvMovedName.textSize = f tvMovedName.textSize = f
tvMoved.textSize = f tvMoved.textSize = f
tvPersonalNotes.textSize = f
} }
f = activity.acct_font_size_sp f = activity.acct_font_size_sp
@ -159,6 +177,7 @@ internal class ViewHolderHeaderProfile(
val contentColor = column.getContentColor() val contentColor = column.getContentColor()
this.contentColor = contentColor this.contentColor = contentColor
tvPersonalNotes.textColor = contentColor
tvMoved.textColor = contentColor tvMoved.textColor = contentColor
tvMovedName.textColor = contentColor tvMovedName.textColor = contentColor
tvDisplayName.textColor = contentColor tvDisplayName.textColor = contentColor
@ -175,7 +194,15 @@ internal class ViewHolderHeaderProfile(
color = contentColor, color = contentColor,
alphaMultiplier = Styler.boost_alpha alphaMultiplier = Styler.boost_alpha
) )
setIconDrawableId(
activity,
btnPersonalNotesEdit,
R.drawable.ic_edit,
color = contentColor,
alphaMultiplier = Styler.boost_alpha
)
val acctColor = column.getAcctColor() val acctColor = column.getAcctColor()
tvCreated.textColor = acctColor tvCreated.textColor = acctColor
tvMovedAcct.textColor = acctColor tvMovedAcct.textColor = acctColor
@ -200,6 +227,7 @@ internal class ViewHolderHeaderProfile(
llFields.removeAllViews() llFields.removeAllViews()
if(who == null) { if(who == null) {
relation = null
tvCreated.text = "" tvCreated.text = ""
tvLastStatusAt.vg(false) tvLastStatusAt.vg(false)
ivBackground.setImageDrawable(null) ivBackground.setImageDrawable(null)
@ -345,6 +373,8 @@ internal class ViewHolderHeaderProfile(
} }
val relation = UserRelation.load(access_info.db_id, who.id) val relation = UserRelation.load(access_info.db_id, who.id)
this.relation = relation
Styler.setFollowIcon( Styler.setFollowIcon(
activity, activity,
btnFollow, btnFollow,
@ -355,6 +385,8 @@ internal class ViewHolderHeaderProfile(
alphaMultiplier = Styler.boost_alpha alphaMultiplier = Styler.boost_alpha
) )
tvPersonalNotes.text = relation.note ?: ""
showMoved(who, who.movedRef) showMoved(who, who.movedRef)
val fields = whoDetail?.fields ?: who.fields 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)
}
}
})
}
}
)
}
} }
} }

View File

@ -93,7 +93,7 @@ object Action_Account {
activity, activity,
activity.getString(R.string.access_token_or_api_token), activity.getString(R.string.access_token_or_api_token),
null, null,
object : DlgTextInput.Callback { callback = object : DlgTextInput.Callback {
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun onOK( override fun onOK(

View File

@ -140,7 +140,7 @@ object Action_List {
activity, activity,
activity.getString(R.string.rename), activity.getString(R.string.rename),
item.title, item.title,
object : DlgTextInput.Callback { callback = object : DlgTextInput.Callback {
override fun onEmptyError() { override fun onEmptyError() {
showToast(activity, false, R.string.list_name_empty) showToast(activity, false, R.string.list_name_empty)
} }

View File

@ -39,6 +39,9 @@ class TootRelationShip(parser:TootParser,src : JsonObject) {
// misskey用 // misskey用
val requested_by : Boolean val requested_by : Boolean
// (Mastodon 3.2)
var note : String? = null
init { init {
@ -91,6 +94,7 @@ class TootRelationShip(parser:TootParser,src : JsonObject) {
this.muting = src.optBoolean("muting") this.muting = src.optBoolean("muting")
this.requested = src.optBoolean("requested") this.requested = src.optBoolean("requested")
this.endorsed = src.optBoolean("endorsed") this.endorsed = src.optBoolean("endorsed")
this.note = src.optString( "note")
// https://github.com/tootsuite/mastodon/commit/9745de883b198375ba23f7fde879f6d75ce2df0f // https://github.com/tootsuite/mastodon/commit/9745de883b198375ba23f7fde879f6d75ce2df0f
// Mastodon 2.8.0から // Mastodon 2.8.0から

View File

@ -265,7 +265,7 @@ class DlgListMember(
activity, activity,
activity.getString(R.string.list_create), activity.getString(R.string.list_create),
null, null,
object : DlgTextInput.Callback { callback = object : DlgTextInput.Callback {
override fun onEmptyError() { override fun onEmptyError() {
showToast(activity, false, R.string.list_name_empty) showToast(activity, false, R.string.list_name_empty)

View File

@ -24,6 +24,7 @@ object DlgTextInput {
activity : Activity, activity : Activity,
caption : CharSequence, caption : CharSequence,
initial_text : CharSequence?, initial_text : CharSequence?,
allowEmpty : Boolean = false,
callback : Callback callback : Callback
) { ) {
val view = activity.layoutInflater.inflate(R.layout.dlg_text_input, null, false) val view = activity.layoutInflater.inflate(R.layout.dlg_text_input, null, false)
@ -51,7 +52,7 @@ object DlgTextInput {
btnOk.setOnClickListener { btnOk.setOnClickListener {
val token = etInput.text.toString().trim { it <= ' ' } val token = etInput.text.toString().trim { it <= ' ' }
if( token.isEmpty() ) { if( token.isEmpty() && !allowEmpty ) {
callback.onEmptyError() callback.onEmptyError()
} else { } else {
callback.onOK(dialog, token) callback.onOK(dialog, token)

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter.table package jp.juggler.subwaytooter.table
import android.content.ContentValues
import android.database.Cursor import android.database.Cursor
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
@ -21,3 +22,6 @@ interface TableCompanion {
fun onDBCreate(db : SQLiteDatabase) fun onDBCreate(db : SQLiteDatabase)
fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int) fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int)
} }
fun ContentValues.putOrNull(key : String, value : String?) =
if(value == null) putNull(key) else put(key, value)

View File

@ -13,6 +13,7 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip
import jp.juggler.util.JsonObject import jp.juggler.util.JsonObject
import jp.juggler.util.LogCategory import jp.juggler.util.LogCategory
import jp.juggler.util.getInt import jp.juggler.util.getInt
import jp.juggler.util.getStringOrNull
class UserRelation { class UserRelation {
@ -26,6 +27,8 @@ class UserRelation {
var following_reblogs : Int = 0 // このユーザからのブーストをTLに表示する var following_reblogs : Int = 0 // このユーザからのブーストをTLに表示する
var endorsed : Boolean = false // ユーザをプロフィールで紹介する var endorsed : Boolean = false // ユーザをプロフィールで紹介する
var note : String? = null
// 認証ユーザからのフォロー状態 // 認証ユーザからのフォロー状態
fun getFollowing(who : TootAccount?) : Boolean { fun getFollowing(who : TootAccount?) : Boolean {
return if(requested && ! following && who != null && ! who.locked) true else following 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_ENDORSED = "endorsed"
private const val COL_BLOCKED_BY = "blocked_by" private const val COL_BLOCKED_BY = "blocked_by"
private const val COL_REQUESTED_BY = "requested_by" private const val COL_REQUESTED_BY = "requested_by"
private const val COL_NOTE = "note"
private const val DB_ID_PSEUDO = - 2L private const val DB_ID_PSEUDO = - 2L
@ -83,6 +87,7 @@ class UserRelation {
,$COL_ENDORSED integer default 0 ,$COL_ENDORSED integer default 0
,$COL_BLOCKED_BY integer default 0 ,$COL_BLOCKED_BY integer default 0
,$COL_REQUESTED_BY integer default 0 ,$COL_REQUESTED_BY integer default 0
,$COL_NOTE text default null
)""" )"""
) )
db.execSQL( db.execSQL(
@ -119,6 +124,13 @@ class UserRelation {
log.trace(ex) 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) { fun deleteOld(now : Long) {
@ -155,6 +167,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
App1.database.replaceOrThrow(table, null, cv) App1.database.replaceOrThrow(table, null, cv)
val key = String.format("%s:%s", db_id, whoId) 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_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
App1.database.replaceOrThrow(table, null, cv) App1.database.replaceOrThrow(table, null, cv)
val key = String.format("%s:%s", db_id, id) val key = String.format("%s:%s", db_id, id)
mMemoryCache.remove(key) mMemoryCache.remove(key)
@ -216,6 +230,7 @@ class UserRelation {
cv.put(COL_REQUESTED, src.requested.b2i()) cv.put(COL_REQUESTED, src.requested.b2i())
cv.put(COL_FOLLOWING_REBLOGS, src.showing_reblogs) cv.put(COL_FOLLOWING_REBLOGS, src.showing_reblogs)
cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replaceOrThrow(table, null, cv) db.replaceOrThrow(table, null, cv)
} }
@ -267,6 +282,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replaceOrThrow(table, null, cv) db.replaceOrThrow(table, null, cv)
} }
bOK = true bOK = true
@ -307,6 +323,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i()) cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i()) cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i()) cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replace(table, null, cv) db.replace(table, null, cv)
} }
bOK = true bOK = true
@ -365,6 +382,7 @@ class UserRelation {
dst.endorsed = cursor.getBoolean(COL_ENDORSED) dst.endorsed = cursor.getBoolean(COL_ENDORSED)
dst.blocked_by = cursor.getBoolean(COL_BLOCKED_BY) dst.blocked_by = cursor.getBoolean(COL_BLOCKED_BY)
dst.requested_by = cursor.getBoolean(COL_REQUESTED_BY) dst.requested_by = cursor.getBoolean(COL_REQUESTED_BY)
dst.note = cursor.getStringOrNull(COL_NOTE)
return dst return dst
} }
} }

View File

@ -220,6 +220,39 @@
tools:text="xxxx-xx-xx xx:xx:xx" tools:text="xxxx-xx-xx xx:xx:xx"
/> />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="start"
android:text="@string/personal_notes"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|top"
android:orientation="horizontal"
>
<TextView
android:id="@+id/tvPersonalNotes"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="start"
android:padding="12dp"
android:text="@string/personal_notes"
/>
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_bg_transparent_round6dp"
android:layout_marginStart="4dp"
android:id="@+id/btnPersonalNotesEdit"
android:src="@drawable/ic_edit"
/>
</LinearLayout>
<TextView <TextView
android:id="@+id/tvRemoteProfileWarning" android:id="@+id/tvRemoteProfileWarning"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1028,4 +1028,6 @@
<string name="verified_link_background_color">認証済みリンクの背景色 (アプリ再起動が必要)</string> <string name="verified_link_background_color">認証済みリンクの背景色 (アプリ再起動が必要)</string>
<string name="verified_link_foreground_color">認証済みリンクの文字色 (アプリ再起動が必要)</string> <string name="verified_link_foreground_color">認証済みリンクの文字色 (アプリ再起動が必要)</string>
<string name="screen_bottom_padding">画面下部の余白(単位:dp。デフォルト:0。アプリ再起動が必要)</string> <string name="screen_bottom_padding">画面下部の余白(単位:dp。デフォルト:0。アプリ再起動が必要)</string>
<string name="personal_notes">覚え書き</string>
<string name="personal_notes_of">%1$sに関する覚え書き</string>
</resources> </resources>

View File

@ -1035,4 +1035,6 @@
<string name="verified_link_background_color">Verified link background color (app restart required)</string> <string name="verified_link_background_color">Verified link background color (app restart required)</string>
<string name="verified_link_foreground_color">Verified link foreground color (app restart required)</string> <string name="verified_link_foreground_color">Verified link foreground color (app restart required)</string>
<string name="screen_bottom_padding">Screen bottom padding (Unit: dp. Default:0. App restart required)</string> <string name="screen_bottom_padding">Screen bottom padding (Unit: dp. Default:0. App restart required)</string>
<string name="personal_notes">Personal notes</string>
<string name="personal_notes_of">Personal notes of %1$s</string>
</resources> </resources>