アプリ設定/挙動に「翻訳ボタンを表示する」を追加
This commit is contained in:
parent
4f43dba3c4
commit
0396264e27
@ -256,6 +256,8 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".ChooseReceiver" android:exported="false"/>
|
||||||
|
|
||||||
<!-- okhttp3クライアントを指定する必要があるため、マニフェスト経由での組み込みは行わない -->
|
<!-- okhttp3クライアントを指定する必要があるため、マニフェスト経由での組み込みは行わない -->
|
||||||
<!--<meta-data-->
|
<!--<meta-data-->
|
||||||
<!--android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"-->
|
<!--android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"-->
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package jp.juggler.subwaytooter
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
@ -27,6 +28,7 @@ import org.jetbrains.anko.backgroundDrawable
|
|||||||
import org.jetbrains.anko.textColor
|
import org.jetbrains.anko.textColor
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -157,6 +159,7 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
private var etRoundRatio : EditText? = null
|
private var etRoundRatio : EditText? = null
|
||||||
private var etBoostAlpha : EditText? = null
|
private var etBoostAlpha : EditText? = null
|
||||||
private var etMediaReadTimeout : EditText? = null
|
private var etMediaReadTimeout : EditText? = null
|
||||||
|
private var etTranslateAppComponent : EditText? = null
|
||||||
|
|
||||||
private var tvTimelineFontUrl : TextView? = null
|
private var tvTimelineFontUrl : TextView? = null
|
||||||
private var timeline_font : String? = null
|
private var timeline_font : String? = null
|
||||||
@ -201,6 +204,13 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
private var hasLinkColorUi = false
|
private var hasLinkColorUi = false
|
||||||
private var hasColumnColorDefaultUi = false
|
private var hasColumnColorDefaultUi = false
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
checkIntentChoiced()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
|
||||||
@ -378,6 +388,8 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
, R.id.btnBackgroundColorVotedReset
|
, R.id.btnBackgroundColorVotedReset
|
||||||
, R.id.btnBackgroundColorFollowRequestedEdit
|
, R.id.btnBackgroundColorFollowRequestedEdit
|
||||||
, R.id.btnBackgroundColorFollowRequestedReset
|
, R.id.btnBackgroundColorFollowRequestedReset
|
||||||
|
, R.id.btnTranslateAppComponentEdit
|
||||||
|
, R.id.btnTranslateAppComponentReset
|
||||||
).forEach {
|
).forEach {
|
||||||
findViewById<View>(it)?.setOnClickListener(this)
|
findViewById<View>(it)?.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
@ -432,6 +444,9 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
etMediaReadTimeout = findViewById(R.id.etMediaReadTimeout)
|
etMediaReadTimeout = findViewById(R.id.etMediaReadTimeout)
|
||||||
etMediaReadTimeout?.addTextChangedListener(this)
|
etMediaReadTimeout?.addTextChangedListener(this)
|
||||||
|
|
||||||
|
etTranslateAppComponent = findViewById(R.id.etTranslateAppComponent)
|
||||||
|
etTranslateAppComponent?.addTextChangedListener(this)
|
||||||
|
|
||||||
tvTimelineFontSize = findViewById(R.id.tvTimelineFontSize)
|
tvTimelineFontSize = findViewById(R.id.tvTimelineFontSize)
|
||||||
tvAcctFontSize = findViewById(R.id.tvAcctFontSize)
|
tvAcctFontSize = findViewById(R.id.tvAcctFontSize)
|
||||||
tvNotificationTlFontSize = findViewById(R.id.tvNotificationTlFontSize)
|
tvNotificationTlFontSize = findViewById(R.id.tvNotificationTlFontSize)
|
||||||
@ -587,6 +602,7 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
etBoostAlpha?.setText(Pref.spBoostAlpha(pref))
|
etBoostAlpha?.setText(Pref.spBoostAlpha(pref))
|
||||||
|
|
||||||
etMediaReadTimeout?.setText(Pref.spMediaReadTimeout(pref))
|
etMediaReadTimeout?.setText(Pref.spMediaReadTimeout(pref))
|
||||||
|
etTranslateAppComponent?.setText(Pref.spTranslateAppComponent(pref))
|
||||||
|
|
||||||
timeline_font = Pref.spTimelineFont(pref)
|
timeline_font = Pref.spTimelineFont(pref)
|
||||||
timeline_font_bold = Pref.spTimelineFontBold(pref)
|
timeline_font_bold = Pref.spTimelineFontBold(pref)
|
||||||
@ -674,6 +690,7 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
putText(Pref.spRoundRatio, etRoundRatio)
|
putText(Pref.spRoundRatio, etRoundRatio)
|
||||||
putText(Pref.spBoostAlpha, etBoostAlpha)
|
putText(Pref.spBoostAlpha, etBoostAlpha)
|
||||||
putText(Pref.spMediaReadTimeout, etMediaReadTimeout)
|
putText(Pref.spMediaReadTimeout, etMediaReadTimeout)
|
||||||
|
putText(Pref.spTranslateAppComponent,etTranslateAppComponent)
|
||||||
|
|
||||||
fun putIf(hasUi : Boolean, sp : StringPref, value : String) {
|
fun putIf(hasUi : Boolean, sp : StringPref, value : String) {
|
||||||
if(! hasUi) return
|
if(! hasUi) return
|
||||||
@ -1114,6 +1131,54 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
saveUIToData()
|
saveUIToData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.btnTranslateAppComponentEdit -> {
|
||||||
|
|
||||||
|
val intent = Intent()
|
||||||
|
intent.action = Intent.ACTION_SEND
|
||||||
|
intent.type = "text/plain"
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.content_sample))
|
||||||
|
|
||||||
|
// このifはwhenにしてはならない。APIバージョン関連の警告が出てしまう
|
||||||
|
@Suppress("CascadeIf")
|
||||||
|
if(intent.resolveActivity(packageManager) == null) {
|
||||||
|
// ACTION_SENDを受け取れるアプリがインストールされてない
|
||||||
|
showToast(this, true, getString(R.string.missing_app_can_receive_action_send))
|
||||||
|
} else if(Build.VERSION.SDK_INT <= 21) {
|
||||||
|
// createChooserにIntentSenderを指定できるのはAndroid 22以降
|
||||||
|
showToast(
|
||||||
|
this,
|
||||||
|
true,
|
||||||
|
getString(R.string.translation_app_chooser_works_android_5_1)
|
||||||
|
)
|
||||||
|
} else try {
|
||||||
|
ChooseReceiver.lastComponentName = null
|
||||||
|
ChooseReceiver.setCallback{ checkIntentChoiced() }
|
||||||
|
|
||||||
|
val receiver = Intent(this, ChooseReceiver::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
this,
|
||||||
|
1,
|
||||||
|
receiver,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
startActivity(
|
||||||
|
Intent.createChooser(
|
||||||
|
intent,
|
||||||
|
getString(R.string.select_translate_app),
|
||||||
|
pendingIntent.intentSender
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
} catch(ex : Throwable) {
|
||||||
|
log.trace(ex)
|
||||||
|
showToast(this, ex, "btnTranslateAppComponentEdit failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.btnTranslateAppComponentReset -> {
|
||||||
|
etTranslateAppComponent?.setText("")
|
||||||
|
saveUIToData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1642,4 +1707,16 @@ class ActAppSettingChild : AppCompatActivity()
|
|||||||
return list[position].id
|
return list[position].id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkIntentChoiced(){
|
||||||
|
if( isDestroyed ) return
|
||||||
|
|
||||||
|
val cn = ChooseReceiver.lastComponentName
|
||||||
|
if(cn != null && etTranslateAppComponent != null) {
|
||||||
|
etTranslateAppComponent?.setText("${cn.packageName}/${cn.className}")
|
||||||
|
saveUIToData()
|
||||||
|
ChooseReceiver.lastComponentName = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
package jp.juggler.subwaytooter
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.table.MutedWord
|
import jp.juggler.subwaytooter.table.MutedWord
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
import jp.juggler.subwaytooter.util.TootTextEncoder
|
||||||
import jp.juggler.util.LogCategory
|
import jp.juggler.util.LogCategory
|
||||||
import jp.juggler.util.copyToClipboard
|
import jp.juggler.util.copyToClipboard
|
||||||
import jp.juggler.util.hideKeyboard
|
import jp.juggler.util.hideKeyboard
|
||||||
import jp.juggler.util.showToast
|
import jp.juggler.util.showToast
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ActText : AppCompatActivity(), View.OnClickListener {
|
class ActText : AppCompatActivity(), View.OnClickListener {
|
||||||
|
|
||||||
@ -32,281 +31,6 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||||||
internal const val EXTRA_CONTENT_END = "content_end"
|
internal const val EXTRA_CONTENT_END = "content_end"
|
||||||
internal const val EXTRA_ACCOUNT_DB_ID = "account_db_id"
|
internal const val EXTRA_ACCOUNT_DB_ID = "account_db_id"
|
||||||
|
|
||||||
private fun StringBuilder.addAfterLine( text : CharSequence) {
|
|
||||||
if( isNotEmpty() && this[length - 1] != '\n') {
|
|
||||||
append('\n')
|
|
||||||
}
|
|
||||||
append(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addHeader(
|
|
||||||
context : Context,
|
|
||||||
sb : StringBuilder,
|
|
||||||
key_str_id : Int,
|
|
||||||
value : Any?
|
|
||||||
) {
|
|
||||||
if(sb.isNotEmpty() && sb[sb.length - 1] != '\n') {
|
|
||||||
sb.append('\n')
|
|
||||||
}
|
|
||||||
sb.addAfterLine( context.getString(key_str_id))
|
|
||||||
sb.append(": ")
|
|
||||||
sb.append(value?.toString() ?: "(null)")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encodeStatus(
|
|
||||||
intent : Intent,
|
|
||||||
context : Context,
|
|
||||||
access_info : SavedAccount,
|
|
||||||
status : TootStatus
|
|
||||||
) {
|
|
||||||
val sb = StringBuilder()
|
|
||||||
|
|
||||||
addHeader(context, sb, R.string.send_header_url, status.url)
|
|
||||||
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_date,
|
|
||||||
TootStatus.formatTime(context, status.time_created_at, false)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_from_acct,
|
|
||||||
access_info.getFullAcct(status.account)
|
|
||||||
)
|
|
||||||
|
|
||||||
val sv : String? = status.spoiler_text
|
|
||||||
if(sv != null && sv.isNotEmpty()) {
|
|
||||||
addHeader(context, sb, R.string.send_header_content_warning, sv)
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.addAfterLine( "\n")
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_CONTENT_START, sb.length)
|
|
||||||
sb.append(DecodeOptions(context, access_info).decodeHTML(status.content))
|
|
||||||
|
|
||||||
encodePolls(sb,context,status)
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_CONTENT_END, sb.length)
|
|
||||||
|
|
||||||
dumpAttachment(sb, status.media_attachments)
|
|
||||||
|
|
||||||
sb.addAfterLine( String.format(Locale.JAPAN, "Status-Source: %s", status.json))
|
|
||||||
|
|
||||||
sb.addAfterLine( "")
|
|
||||||
intent.putExtra(EXTRA_TEXT, sb.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun dumpAttachment(sb : StringBuilder, src : ArrayList<TootAttachmentLike>?) {
|
|
||||||
if(src == null) return
|
|
||||||
var i = 0
|
|
||||||
for(ma in src) {
|
|
||||||
++ i
|
|
||||||
if(ma is TootAttachment) {
|
|
||||||
sb.addAfterLine( "\n")
|
|
||||||
sb.addAfterLine( String.format(Locale.JAPAN, "Media-%d-Url: %s", i, ma.url))
|
|
||||||
sb.addAfterLine(
|
|
||||||
String.format(Locale.JAPAN, "Media-%d-Remote-Url: %s", i, ma.remote_url)
|
|
||||||
)
|
|
||||||
sb.addAfterLine(
|
|
||||||
String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
|
|
||||||
)
|
|
||||||
sb. addAfterLine(
|
|
||||||
String.format(Locale.JAPAN, "Media-%d-Text-Url: %s", i, ma.text_url)
|
|
||||||
)
|
|
||||||
} else if(ma is TootAttachmentMSP) {
|
|
||||||
sb.addAfterLine( "\n")
|
|
||||||
sb. addAfterLine(
|
|
||||||
String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun encodePolls(sb :StringBuilder, context:Context,status : TootStatus) {
|
|
||||||
val enquete = status.enquete ?: return
|
|
||||||
val items = enquete.items ?: return
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val canVote = when(enquete.pollType) {
|
|
||||||
|
|
||||||
// friends.nico の場合は本文に投票の選択肢が含まれるので
|
|
||||||
// アプリ側での文字列化は不要
|
|
||||||
TootPollsType.FriendsNico -> return
|
|
||||||
|
|
||||||
// MastodonとMisskeyは投票の選択肢が本文に含まれないので
|
|
||||||
// アプリ側で文字列化する
|
|
||||||
|
|
||||||
TootPollsType.Mastodon -> when {
|
|
||||||
enquete.expired -> false
|
|
||||||
now >= enquete.expired_at -> false
|
|
||||||
enquete.myVoted != null -> false
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
TootPollsType.Misskey -> enquete.myVoted == null
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.addAfterLine("\n")
|
|
||||||
|
|
||||||
items.forEachIndexed { index, choice ->
|
|
||||||
encodePollChoice(sb, context, enquete, canVote, index, choice)
|
|
||||||
}
|
|
||||||
|
|
||||||
when(enquete.pollType) {
|
|
||||||
TootPollsType.Mastodon -> encodePollFooterMastodon(sb, context, enquete)
|
|
||||||
|
|
||||||
else->{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encodePollChoice(
|
|
||||||
sb : StringBuilder,
|
|
||||||
context : Context,
|
|
||||||
enquete : TootPolls,
|
|
||||||
canVote : Boolean,
|
|
||||||
i : Int,
|
|
||||||
item : TootPollsChoice
|
|
||||||
) {
|
|
||||||
|
|
||||||
val text = when(enquete.pollType) {
|
|
||||||
TootPollsType.Misskey -> {
|
|
||||||
val sb2 = StringBuilder().append(item.decoded_text)
|
|
||||||
if(enquete.myVoted != null) {
|
|
||||||
sb2.append(" / ")
|
|
||||||
sb2.append(context.getString(R.string.vote_count_text, item.votes))
|
|
||||||
if(i == enquete.myVoted) sb2.append(' ').append(0x2713.toChar())
|
|
||||||
}
|
|
||||||
sb2
|
|
||||||
}
|
|
||||||
|
|
||||||
TootPollsType.FriendsNico -> {
|
|
||||||
item.decoded_text
|
|
||||||
}
|
|
||||||
|
|
||||||
TootPollsType.Mastodon -> if(canVote) {
|
|
||||||
item.decoded_text
|
|
||||||
} else {
|
|
||||||
val sb2 = StringBuilder().append(item.decoded_text)
|
|
||||||
if(! canVote) {
|
|
||||||
sb2.append(" / ")
|
|
||||||
sb2.append(
|
|
||||||
when(val v = item.votes) {
|
|
||||||
null -> context.getString(R.string.vote_count_unavailable)
|
|
||||||
else -> context.getString(R.string.vote_count_text, v)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sb2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.addAfterLine(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encodePollFooterMastodon(
|
|
||||||
sb : StringBuilder,
|
|
||||||
context : Context,
|
|
||||||
enquete : TootPolls
|
|
||||||
) {
|
|
||||||
val line = StringBuilder()
|
|
||||||
|
|
||||||
val votes_count = enquete.votes_count ?: 0
|
|
||||||
when {
|
|
||||||
votes_count == 1 -> line.append(context.getString(R.string.vote_1))
|
|
||||||
votes_count > 1 -> line.append(context.getString(R.string.vote_2, votes_count))
|
|
||||||
}
|
|
||||||
|
|
||||||
when(val t = enquete.expired_at) {
|
|
||||||
|
|
||||||
Long.MAX_VALUE -> {
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
if(line.isNotEmpty()) line.append(" ")
|
|
||||||
line.append(
|
|
||||||
context.getString(
|
|
||||||
R.string.vote_expire_at,
|
|
||||||
TootStatus.formatTime(context, t, false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.addAfterLine(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encodeAccount(
|
|
||||||
intent : Intent,
|
|
||||||
context : Context,
|
|
||||||
access_info : SavedAccount,
|
|
||||||
who : TootAccount
|
|
||||||
) {
|
|
||||||
val sb = StringBuilder()
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_CONTENT_START, sb.length)
|
|
||||||
sb.append(who.display_name)
|
|
||||||
sb.append("\n")
|
|
||||||
sb.append("@")
|
|
||||||
sb.append(access_info.getFullAcct(who))
|
|
||||||
sb.append("\n")
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_CONTENT_START, sb.length)
|
|
||||||
sb.append(who.url)
|
|
||||||
intent.putExtra(EXTRA_CONTENT_END, sb.length)
|
|
||||||
|
|
||||||
sb.addAfterLine( "\n")
|
|
||||||
|
|
||||||
sb.append(DecodeOptions(context, access_info).decodeHTML(who.note))
|
|
||||||
|
|
||||||
sb.addAfterLine( "\n")
|
|
||||||
|
|
||||||
addHeader(context, sb, R.string.send_header_account_name, who.display_name)
|
|
||||||
addHeader(context, sb, R.string.send_header_account_acct, access_info.getFullAcct(who))
|
|
||||||
addHeader(context, sb, R.string.send_header_account_url, who.url)
|
|
||||||
|
|
||||||
addHeader(context, sb, R.string.send_header_account_image_avatar, who.avatar)
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_account_image_avatar_static,
|
|
||||||
who.avatar_static
|
|
||||||
)
|
|
||||||
addHeader(context, sb, R.string.send_header_account_image_header, who.header)
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_account_image_header_static,
|
|
||||||
who.header_static
|
|
||||||
)
|
|
||||||
|
|
||||||
addHeader(context, sb, R.string.send_header_account_created_at, who.created_at)
|
|
||||||
addHeader(context, sb, R.string.send_header_account_statuses_count, who.statuses_count)
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_account_followers_count,
|
|
||||||
who.followers_count
|
|
||||||
)
|
|
||||||
addHeader(
|
|
||||||
context,
|
|
||||||
sb,
|
|
||||||
R.string.send_header_account_following_count,
|
|
||||||
who.following_count
|
|
||||||
)
|
|
||||||
addHeader(context, sb, R.string.send_header_account_locked, who.locked)
|
|
||||||
|
|
||||||
sb.addAfterLine("")
|
|
||||||
intent.putExtra(EXTRA_TEXT, sb.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun open(
|
fun open(
|
||||||
activity : ActMain,
|
activity : ActMain,
|
||||||
request_code : Int,
|
request_code : Int,
|
||||||
@ -315,8 +39,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||||||
) {
|
) {
|
||||||
val intent = Intent(activity, ActText::class.java)
|
val intent = Intent(activity, ActText::class.java)
|
||||||
intent.putExtra(EXTRA_ACCOUNT_DB_ID, access_info.db_id)
|
intent.putExtra(EXTRA_ACCOUNT_DB_ID, access_info.db_id)
|
||||||
encodeStatus(intent, activity, access_info, status)
|
TootTextEncoder.encodeStatus(intent, activity, access_info, status)
|
||||||
|
|
||||||
activity.startActivityForResult(intent, request_code)
|
activity.startActivityForResult(intent, request_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +51,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||||||
) {
|
) {
|
||||||
val intent = Intent(activity, ActText::class.java)
|
val intent = Intent(activity, ActText::class.java)
|
||||||
intent.putExtra(EXTRA_ACCOUNT_DB_ID, access_info.db_id)
|
intent.putExtra(EXTRA_ACCOUNT_DB_ID, access_info.db_id)
|
||||||
encodeAccount(intent, activity, access_info, who)
|
TootTextEncoder.encodeAccount(intent, activity, access_info, who)
|
||||||
|
|
||||||
activity.startActivityForResult(intent, request_code)
|
activity.startActivityForResult(intent, request_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,11 +90,11 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||||||
etText.setText(sv)
|
etText.setText(sv)
|
||||||
|
|
||||||
// Android 9 以降ではフォーカスがないとsetSelectionできない
|
// Android 9 以降ではフォーカスがないとsetSelectionできない
|
||||||
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
etText.requestFocus()
|
etText.requestFocus()
|
||||||
etText.hideKeyboard()
|
etText.hideKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
etText.setSelection(content_start, content_end)
|
etText.setSelection(content_start, content_end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
app/src/main/java/jp/juggler/subwaytooter/ChooseReceiver.kt
Normal file
24
app/src/main/java/jp/juggler/subwaytooter/ChooseReceiver.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
class ChooseReceiver :BroadcastReceiver(){
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
var lastComponentName: ComponentName? = null
|
||||||
|
var refCallback : WeakReference<()->Unit>? = null
|
||||||
|
|
||||||
|
fun setCallback(cb:()->Unit){
|
||||||
|
refCallback = WeakReference(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReceive(context: Context,intent: Intent?) {
|
||||||
|
lastComponentName = intent?.extras?.get(Intent.EXTRA_CHOSEN_COMPONENT) as? ComponentName
|
||||||
|
refCallback?.get()?.invoke()
|
||||||
|
}
|
||||||
|
}
|
@ -394,6 +394,13 @@ object Pref {
|
|||||||
R.id.swCustomEmojiSeparatorZwsp
|
R.id.swCustomEmojiSeparatorZwsp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val bpShowTranslateButton = BooleanPref(
|
||||||
|
"ShowTranslateButton",
|
||||||
|
false,
|
||||||
|
R.id.swShowTranslateButton
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
// int
|
// int
|
||||||
|
|
||||||
val ipBackButtonAction = IntPref("back_button_action", 0)
|
val ipBackButtonAction = IntPref("back_button_action", 0)
|
||||||
@ -493,6 +500,8 @@ object Pref {
|
|||||||
val spQuickTootMacro = StringPref("QuickTootMacro","")
|
val spQuickTootMacro = StringPref("QuickTootMacro","")
|
||||||
val spQuickTootVisibility = StringPref("QuickTootVisibility","")
|
val spQuickTootVisibility = StringPref("QuickTootVisibility","")
|
||||||
|
|
||||||
|
val spTranslateAppComponent = StringPref("TranslateAppComponent","")
|
||||||
|
|
||||||
// long
|
// long
|
||||||
val lpTabletTootDefaultAccount = LongPref("tablet_toot_default_account", - 1L)
|
val lpTabletTootDefaultAccount = LongPref("tablet_toot_default_account", - 1L)
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package jp.juggler.subwaytooter
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.Intent
|
||||||
import androidx.core.content.ContextCompat
|
import android.content.SharedPreferences
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.PopupWindow
|
import android.widget.PopupWindow
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.android.flexbox.FlexWrap
|
import com.google.android.flexbox.FlexWrap
|
||||||
import com.google.android.flexbox.FlexboxLayout
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
import com.google.android.flexbox.JustifyContent
|
import com.google.android.flexbox.JustifyContent
|
||||||
@ -19,6 +21,7 @@ import jp.juggler.subwaytooter.api.entity.TootStatus
|
|||||||
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.table.UserRelation
|
import jp.juggler.subwaytooter.table.UserRelation
|
||||||
|
import jp.juggler.subwaytooter.util.TootTextEncoder
|
||||||
import jp.juggler.subwaytooter.util.startMargin
|
import jp.juggler.subwaytooter.util.startMargin
|
||||||
import jp.juggler.subwaytooter.view.CountImageButton
|
import jp.juggler.subwaytooter.view.CountImageButton
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
@ -37,6 +40,16 @@ internal class StatusButtons(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val log = LogCategory("StatusButtons")
|
val log = LogCategory("StatusButtons")
|
||||||
|
|
||||||
|
fun String.toComponentName() : ComponentName? {
|
||||||
|
try {
|
||||||
|
val idx = indexOf('/')
|
||||||
|
if(idx >= 1) return ComponentName(substring(0 until idx), substring(idx + 1))
|
||||||
|
} catch(ex:Throwable) {
|
||||||
|
log.e(ex,"incorrect component name $this")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val access_info : SavedAccount
|
private val access_info : SavedAccount
|
||||||
@ -53,6 +66,7 @@ internal class StatusButtons(
|
|||||||
private val llFollow2 = holder.llFollow2
|
private val llFollow2 = holder.llFollow2
|
||||||
private val btnFollow2 = holder.btnFollow2
|
private val btnFollow2 = holder.btnFollow2
|
||||||
private val ivFollowedBy2 = holder.ivFollowedBy2
|
private val ivFollowedBy2 = holder.ivFollowedBy2
|
||||||
|
private val btnTranslate = holder.btnTranslate
|
||||||
private val btnMore = holder.btnMore
|
private val btnMore = holder.btnMore
|
||||||
|
|
||||||
private val color_normal = column.getContentColor()
|
private val color_normal = column.getContentColor()
|
||||||
@ -69,6 +83,7 @@ internal class StatusButtons(
|
|||||||
btnFavourite.setOnLongClickListener(this)
|
btnFavourite.setOnLongClickListener(this)
|
||||||
btnFollow2.setOnClickListener(this)
|
btnFollow2.setOnClickListener(this)
|
||||||
btnFollow2.setOnLongClickListener(this)
|
btnFollow2.setOnLongClickListener(this)
|
||||||
|
btnTranslate.setOnClickListener(this)
|
||||||
btnMore.setOnClickListener(this)
|
btnMore.setOnClickListener(this)
|
||||||
btnConversation.setOnClickListener(this)
|
btnConversation.setOnClickListener(this)
|
||||||
btnConversation.setOnLongClickListener(this)
|
btnConversation.setOnLongClickListener(this)
|
||||||
@ -210,6 +225,15 @@ internal class StatusButtons(
|
|||||||
relation
|
relation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(vg(btnTranslate, Pref.bpShowTranslateButton(activity.pref))) {
|
||||||
|
setButton(
|
||||||
|
btnTranslate,
|
||||||
|
true,
|
||||||
|
color_normal,
|
||||||
|
R.drawable.ic_translate,
|
||||||
|
activity.getString(R.string.translate)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setButton(
|
private fun setButton(
|
||||||
@ -234,6 +258,25 @@ internal class StatusButtons(
|
|||||||
b.isEnabled = enabled
|
b.isEnabled = enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setButton(
|
||||||
|
b : ImageButton,
|
||||||
|
enabled : Boolean,
|
||||||
|
color : Int,
|
||||||
|
drawableId : Int,
|
||||||
|
contentDescription : String
|
||||||
|
) {
|
||||||
|
val alpha = Styler.boost_alpha
|
||||||
|
val d = createColoredDrawable(
|
||||||
|
activity,
|
||||||
|
drawableId,
|
||||||
|
color,
|
||||||
|
alpha
|
||||||
|
)
|
||||||
|
b.setImageDrawable(d)
|
||||||
|
b.contentDescription = contentDescription
|
||||||
|
b.isEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
override fun onClick(v : View) {
|
override fun onClick(v : View) {
|
||||||
|
|
||||||
close_window?.dismiss()
|
close_window?.dismiss()
|
||||||
@ -381,6 +424,38 @@ internal class StatusButtons(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btnTranslate -> {
|
||||||
|
|
||||||
|
try {
|
||||||
|
val sv = TootTextEncoder.encodeStatusForTranslate(activity, access_info, status)
|
||||||
|
|
||||||
|
var cn = Pref.spTranslateAppComponent(activity.pref)
|
||||||
|
.toComponentName()
|
||||||
|
if(cn == null) {
|
||||||
|
cn = activity.getString(R.string.translate_app_component_default)
|
||||||
|
.toComponentName()
|
||||||
|
if(cn == null) {
|
||||||
|
showToast(
|
||||||
|
activity,
|
||||||
|
true,
|
||||||
|
"please check translate app component in app setting."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent()
|
||||||
|
intent.action = Intent.ACTION_SEND
|
||||||
|
intent.type = "text/plain"
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, sv)
|
||||||
|
intent.component = cn
|
||||||
|
activity.startActivity(intent)
|
||||||
|
} catch(ex : Throwable) {
|
||||||
|
log.trace(ex)
|
||||||
|
showToast(activity, ex, "send failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btnMore -> DlgContextMenu(
|
btnMore -> DlgContextMenu(
|
||||||
activity,
|
activity,
|
||||||
column,
|
column,
|
||||||
@ -463,6 +538,7 @@ class StatusButtonsViewHolder(
|
|||||||
lateinit var llFollow2 : View
|
lateinit var llFollow2 : View
|
||||||
lateinit var btnFollow2 : ImageButton
|
lateinit var btnFollow2 : ImageButton
|
||||||
lateinit var ivFollowedBy2 : ImageView
|
lateinit var ivFollowedBy2 : ImageView
|
||||||
|
lateinit var btnTranslate : ImageButton
|
||||||
lateinit var btnMore : ImageButton
|
lateinit var btnMore : ImageButton
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -555,6 +631,20 @@ class StatusButtonsViewHolder(
|
|||||||
}.lparams(matchParent, matchParent)
|
}.lparams(matchParent, matchParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btnTranslate = imageButton {
|
||||||
|
background = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.btn_bg_transparent
|
||||||
|
)
|
||||||
|
setPadding(paddingH, paddingV, paddingH, paddingV)
|
||||||
|
scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
|
|
||||||
|
contentDescription = context.getString(R.string.translate)
|
||||||
|
imageResource = R.drawable.ic_translate
|
||||||
|
}.lparams(buttonHeight, buttonHeight) {
|
||||||
|
startMargin = marginBetween
|
||||||
|
}
|
||||||
|
|
||||||
btnMore = imageButton {
|
btnMore = imageButton {
|
||||||
background = ContextCompat.getDrawable(
|
background = ContextCompat.getDrawable(
|
||||||
context,
|
context,
|
||||||
|
@ -0,0 +1,307 @@
|
|||||||
|
package jp.juggler.subwaytooter.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import jp.juggler.subwaytooter.ActText
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object TootTextEncoder {
|
||||||
|
private fun StringBuilder.addAfterLine( text : CharSequence) {
|
||||||
|
if( isNotEmpty() && this[length - 1] != '\n') {
|
||||||
|
append('\n')
|
||||||
|
}
|
||||||
|
append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addHeader(
|
||||||
|
context : Context,
|
||||||
|
sb : StringBuilder,
|
||||||
|
key_str_id : Int,
|
||||||
|
value : Any?
|
||||||
|
) {
|
||||||
|
if(sb.isNotEmpty() && sb[sb.length - 1] != '\n') {
|
||||||
|
sb.append('\n')
|
||||||
|
}
|
||||||
|
sb.addAfterLine( context.getString(key_str_id))
|
||||||
|
sb.append(": ")
|
||||||
|
sb.append(value?.toString() ?: "(null)")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encodeStatus(
|
||||||
|
intent : Intent,
|
||||||
|
context : Context,
|
||||||
|
access_info : SavedAccount,
|
||||||
|
status : TootStatus
|
||||||
|
) {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
|
||||||
|
addHeader(context, sb, R.string.send_header_url, status.url)
|
||||||
|
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_date,
|
||||||
|
TootStatus.formatTime(context, status.time_created_at, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_from_acct,
|
||||||
|
access_info.getFullAcct(status.account)
|
||||||
|
)
|
||||||
|
|
||||||
|
val sv : String? = status.spoiler_text
|
||||||
|
if(sv != null && sv.isNotEmpty()) {
|
||||||
|
addHeader(context, sb, R.string.send_header_content_warning, sv)
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.addAfterLine( "\n")
|
||||||
|
|
||||||
|
intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
|
||||||
|
sb.append(DecodeOptions(context, access_info).decodeHTML(status.content))
|
||||||
|
|
||||||
|
encodePolls(sb,context,status)
|
||||||
|
|
||||||
|
intent.putExtra(ActText.EXTRA_CONTENT_END, sb.length)
|
||||||
|
|
||||||
|
dumpAttachment(sb, status.media_attachments)
|
||||||
|
|
||||||
|
sb.addAfterLine( String.format(Locale.JAPAN, "Status-Source: %s", status.json))
|
||||||
|
|
||||||
|
sb.addAfterLine( "")
|
||||||
|
intent.putExtra(ActText.EXTRA_TEXT, sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun encodeStatusForTranslate(
|
||||||
|
context : Context,
|
||||||
|
access_info : SavedAccount,
|
||||||
|
status : TootStatus
|
||||||
|
) :String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
|
||||||
|
val sv : String? = status.spoiler_text
|
||||||
|
if(sv != null && sv.isNotEmpty()) {
|
||||||
|
sb.append(sv).append("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(DecodeOptions(context, access_info).decodeHTML(status.content))
|
||||||
|
|
||||||
|
encodePolls(sb,context,status)
|
||||||
|
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun dumpAttachment(sb : StringBuilder, src : ArrayList<TootAttachmentLike>?) {
|
||||||
|
if(src == null) return
|
||||||
|
var i = 0
|
||||||
|
for(ma in src) {
|
||||||
|
++ i
|
||||||
|
if(ma is TootAttachment) {
|
||||||
|
sb.addAfterLine( "\n")
|
||||||
|
sb.addAfterLine( String.format(Locale.JAPAN, "Media-%d-Url: %s", i, ma.url))
|
||||||
|
sb.addAfterLine(
|
||||||
|
String.format(Locale.JAPAN, "Media-%d-Remote-Url: %s", i, ma.remote_url)
|
||||||
|
)
|
||||||
|
sb.addAfterLine(
|
||||||
|
String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
|
||||||
|
)
|
||||||
|
sb. addAfterLine(
|
||||||
|
String.format(Locale.JAPAN, "Media-%d-Text-Url: %s", i, ma.text_url)
|
||||||
|
)
|
||||||
|
} else if(ma is TootAttachmentMSP) {
|
||||||
|
sb.addAfterLine( "\n")
|
||||||
|
sb. addAfterLine(
|
||||||
|
String.format(Locale.JAPAN, "Media-%d-Preview-Url: %s", i, ma.preview_url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun encodePolls(sb :StringBuilder, context: Context, status : TootStatus) {
|
||||||
|
val enquete = status.enquete ?: return
|
||||||
|
val items = enquete.items ?: return
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val canVote = when(enquete.pollType) {
|
||||||
|
|
||||||
|
// friends.nico の場合は本文に投票の選択肢が含まれるので
|
||||||
|
// アプリ側での文字列化は不要
|
||||||
|
TootPollsType.FriendsNico -> return
|
||||||
|
|
||||||
|
// MastodonとMisskeyは投票の選択肢が本文に含まれないので
|
||||||
|
// アプリ側で文字列化する
|
||||||
|
|
||||||
|
TootPollsType.Mastodon -> when {
|
||||||
|
enquete.expired -> false
|
||||||
|
now >= enquete.expired_at -> false
|
||||||
|
enquete.myVoted != null -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
TootPollsType.Misskey -> enquete.myVoted == null
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.addAfterLine("\n")
|
||||||
|
|
||||||
|
items.forEachIndexed { index, choice ->
|
||||||
|
encodePollChoice(sb, context, enquete, canVote, index, choice)
|
||||||
|
}
|
||||||
|
|
||||||
|
when(enquete.pollType) {
|
||||||
|
TootPollsType.Mastodon -> encodePollFooterMastodon(sb, context, enquete)
|
||||||
|
|
||||||
|
else->{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun encodePollChoice(
|
||||||
|
sb : StringBuilder,
|
||||||
|
context : Context,
|
||||||
|
enquete : TootPolls,
|
||||||
|
canVote : Boolean,
|
||||||
|
i : Int,
|
||||||
|
item : TootPollsChoice
|
||||||
|
) {
|
||||||
|
|
||||||
|
val text = when(enquete.pollType) {
|
||||||
|
TootPollsType.Misskey -> {
|
||||||
|
val sb2 = StringBuilder().append(item.decoded_text)
|
||||||
|
if(enquete.myVoted != null) {
|
||||||
|
sb2.append(" / ")
|
||||||
|
sb2.append(context.getString(R.string.vote_count_text, item.votes))
|
||||||
|
if(i == enquete.myVoted) sb2.append(' ').append(0x2713.toChar())
|
||||||
|
}
|
||||||
|
sb2
|
||||||
|
}
|
||||||
|
|
||||||
|
TootPollsType.FriendsNico -> {
|
||||||
|
item.decoded_text
|
||||||
|
}
|
||||||
|
|
||||||
|
TootPollsType.Mastodon -> if(canVote) {
|
||||||
|
item.decoded_text
|
||||||
|
} else {
|
||||||
|
val sb2 = StringBuilder().append(item.decoded_text)
|
||||||
|
if(! canVote) {
|
||||||
|
sb2.append(" / ")
|
||||||
|
sb2.append(
|
||||||
|
when(val v = item.votes) {
|
||||||
|
null -> context.getString(R.string.vote_count_unavailable)
|
||||||
|
else -> context.getString(R.string.vote_count_text, v)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sb2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.addAfterLine(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun encodePollFooterMastodon(
|
||||||
|
sb : StringBuilder,
|
||||||
|
context : Context,
|
||||||
|
enquete : TootPolls
|
||||||
|
) {
|
||||||
|
val line = StringBuilder()
|
||||||
|
|
||||||
|
val votes_count = enquete.votes_count ?: 0
|
||||||
|
when {
|
||||||
|
votes_count == 1 -> line.append(context.getString(R.string.vote_1))
|
||||||
|
votes_count > 1 -> line.append(context.getString(R.string.vote_2, votes_count))
|
||||||
|
}
|
||||||
|
|
||||||
|
when(val t = enquete.expired_at) {
|
||||||
|
|
||||||
|
Long.MAX_VALUE -> {
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
if(line.isNotEmpty()) line.append(" ")
|
||||||
|
line.append(
|
||||||
|
context.getString(
|
||||||
|
R.string.vote_expire_at,
|
||||||
|
TootStatus.formatTime(context, t, false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.addAfterLine(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encodeAccount(
|
||||||
|
intent : Intent,
|
||||||
|
context : Context,
|
||||||
|
access_info : SavedAccount,
|
||||||
|
who : TootAccount
|
||||||
|
) {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
|
||||||
|
intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
|
||||||
|
sb.append(who.display_name)
|
||||||
|
sb.append("\n")
|
||||||
|
sb.append("@")
|
||||||
|
sb.append(access_info.getFullAcct(who))
|
||||||
|
sb.append("\n")
|
||||||
|
|
||||||
|
intent.putExtra(ActText.EXTRA_CONTENT_START, sb.length)
|
||||||
|
sb.append(who.url)
|
||||||
|
intent.putExtra(ActText.EXTRA_CONTENT_END, sb.length)
|
||||||
|
|
||||||
|
sb.addAfterLine( "\n")
|
||||||
|
|
||||||
|
sb.append(DecodeOptions(context, access_info).decodeHTML(who.note))
|
||||||
|
|
||||||
|
sb.addAfterLine( "\n")
|
||||||
|
|
||||||
|
addHeader(context, sb, R.string.send_header_account_name, who.display_name)
|
||||||
|
addHeader(context, sb, R.string.send_header_account_acct, access_info.getFullAcct(who))
|
||||||
|
addHeader(context, sb, R.string.send_header_account_url, who.url)
|
||||||
|
|
||||||
|
addHeader(context, sb, R.string.send_header_account_image_avatar, who.avatar)
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_account_image_avatar_static,
|
||||||
|
who.avatar_static
|
||||||
|
)
|
||||||
|
addHeader(context, sb, R.string.send_header_account_image_header, who.header)
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_account_image_header_static,
|
||||||
|
who.header_static
|
||||||
|
)
|
||||||
|
|
||||||
|
addHeader(context, sb, R.string.send_header_account_created_at, who.created_at)
|
||||||
|
addHeader(context, sb, R.string.send_header_account_statuses_count, who.statuses_count)
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_account_followers_count,
|
||||||
|
who.followers_count
|
||||||
|
)
|
||||||
|
addHeader(
|
||||||
|
context,
|
||||||
|
sb,
|
||||||
|
R.string.send_header_account_following_count,
|
||||||
|
who.following_count
|
||||||
|
)
|
||||||
|
addHeader(context, sb, R.string.send_header_account_locked, who.locked)
|
||||||
|
|
||||||
|
sb.addAfterLine("")
|
||||||
|
intent.putExtra(ActText.EXTRA_TEXT, sb.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
9
app/src/main/res/drawable/ic_translate.xml
Normal file
9
app/src/main/res/drawable/ic_translate.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
|
||||||
|
</vector>
|
@ -647,6 +647,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<View style="@style/setting_divider"/>
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -248,6 +248,60 @@
|
|||||||
|
|
||||||
<View style="@style/setting_divider"/>
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:text="@string/show_translate_button"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/swShowTranslateButton"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/setting_row_label"
|
||||||
|
android:labelFor="@+id/etTranslateAppComponent"
|
||||||
|
android:text="@string/translate_app_component"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etTranslateAppComponent"
|
||||||
|
style="@style/setting_horizontal_stretch"
|
||||||
|
android:inputType="text"
|
||||||
|
android:hint="@string/translate_app_component_default"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout style="@style/setting_row_form">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnTranslateAppComponentEdit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/edit"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnTranslateAppComponentReset"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/reset"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<View style="@style/setting_divider"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -907,5 +907,10 @@
|
|||||||
<string name="hashtag_from_acct">ハッシュタグ(特定ユーザから)</string>
|
<string name="hashtag_from_acct">ハッシュタグ(特定ユーザから)</string>
|
||||||
<string name="notifications_from_acct">ユーザからの通知</string>
|
<string name="notifications_from_acct">ユーザからの通知</string>
|
||||||
<string name="notifications_from">%1$sからの通知</string>
|
<string name="notifications_from">%1$sからの通知</string>
|
||||||
|
<string name="show_translate_button">翻訳ボタンを表示する</string>
|
||||||
|
<string name="translate">翻訳</string>
|
||||||
|
<string name="translate_app_component" >翻訳アプリのComponentName</string>
|
||||||
|
<string name="missing_app_can_receive_action_send">ACTION_SENDを受け取れるアプリがありません。</string>
|
||||||
|
<string name="select_translate_app">翻訳アプリを選択した後に設定画面まで戻ってください</string>
|
||||||
|
<string name="translation_app_chooser_works_android_5_1">翻訳アプリを選択する機能はAndroid5.1+で使えます。</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -900,5 +900,12 @@
|
|||||||
<string name="load_preference_from_web_ui">Load some preferences from WebUI</string>
|
<string name="load_preference_from_web_ui">Load some preferences from WebUI</string>
|
||||||
<string name="notifications_from_acct">Notifications from user</string>
|
<string name="notifications_from_acct">Notifications from user</string>
|
||||||
<string name="notifications_from">Notifications from %1$s</string>
|
<string name="notifications_from">Notifications from %1$s</string>
|
||||||
|
<string name="show_translate_button">Show translate button</string>
|
||||||
|
<string name="translate">Translate</string>
|
||||||
|
<string name="translate_app_component" >Translation app component</string>
|
||||||
|
<string name="translate_app_component_default" translatable="false">com.google.android.apps.translate/com.google.android.apps.translate.TranslateActivity</string>
|
||||||
|
<string name="missing_app_can_receive_action_send">Missing app that can receive ACTION_SEND.</string>
|
||||||
|
<string name="select_translate_app">Select translation app, then back to app setting.</string>
|
||||||
|
<string name="translation_app_chooser_works_android_5_1">Sorry, translation app chooser works on Android 5.1+.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user