diff --git a/.idea/dictionaries/tateisu.xml b/.idea/dictionaries/tateisu.xml
index 2222554c..4a474f80 100644
--- a/.idea/dictionaries/tateisu.xml
+++ b/.idea/dictionaries/tateisu.xml
@@ -64,6 +64,7 @@
kenglxn
kotlinx
lateinit
+ latn
lparams
magick
mailto
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d8b50f9c..cc68b6de 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -234,6 +234,12 @@
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
/>
+
+
{ a, b ->
+ when {
+ a.code == TootStatus.LANGUAGE_CODE_DEFAULT -> - 1
+ b.code == TootStatus.LANGUAGE_CODE_DEFAULT -> 1
+ a.code == TootStatus.LANGUAGE_CODE_UNKNOWN -> - 1
+ b.code == TootStatus.LANGUAGE_CODE_UNKNOWN -> 1
+ else -> a.code.compareTo(b.code)
+ }
+ }
+
+ }
+
+ private val languageNameMap by lazy {
+ HashMap().apply {
+
+ // from https://github.com/google/cld3/blob/master/src/task_context_params.cc#L43
+ val languageNamesCld3 = arrayOf(
+ "eo", "co", "eu", "ta", "de", "mt", "ps", "te", "su", "uz", "zh-Latn", "ne",
+ "nl", "sw", "sq", "hmn", "ja", "no", "mn", "so", "ko", "kk", "sl", "ig",
+ "mr", "th", "zu", "ml", "hr", "bs", "lo", "sd", "cy", "hy", "uk", "pt",
+ "lv", "iw", "cs", "vi", "jv", "be", "km", "mk", "tr", "fy", "am", "zh",
+ "da", "sv", "fi", "ht", "af", "la", "id", "fil", "sm", "ca", "el", "ka",
+ "sr", "it", "sk", "ru", "ru-Latn", "bg", "ny", "fa", "haw", "gl", "et",
+ "ms", "gd", "bg-Latn", "ha", "is", "ur", "mi", "hi", "bn", "hi-Latn", "fr",
+ "yi", "hu", "xh", "my", "tg", "ro", "ar", "lb", "el-Latn", "st", "ceb",
+ "kn", "az", "si", "ky", "mg", "en", "gu", "es", "pl", "ja-Latn", "ga", "lt",
+ "sn", "yo", "pa", "ku"
+ )
+
+ for(src1 in languageNamesCld3) {
+ val src2 = src1.replace("-Latn", "")
+ val isLatn = src2 != src1
+ val locale = Locale(src2)
+ log.w("languageNameMap $src1 ${locale.language} ${locale.country} ${locale.displayName}")
+ put(
+ src1, if(isLatn) {
+ "${locale.displayName}(Latn)"
+ } else {
+ locale.displayName
+ }
+ )
+ }
+ put(TootStatus.LANGUAGE_CODE_DEFAULT, getString(R.string.language_code_default))
+ put(TootStatus.LANGUAGE_CODE_UNKNOWN, getString(R.string.language_code_unknown))
+ }
+ }
+
+ private fun getDesc(item : MyItem) : String {
+ val code = item.code
+ return languageNameMap[code] ?: getString(R.string.custom)
+ }
+
+ private var column_index : Int = 0
+ internal lateinit var column : Column
+ internal lateinit var app_state : AppState
+ internal var density : Float = 0f
+
+ private lateinit var listView : ListView
+ private lateinit var adapter : MyAdapter
+ private val languageList = ArrayList()
+ private var loading_busy : Boolean = false
+
+ override fun onCreate(savedInstanceState : Bundle?) {
+ super.onCreate(savedInstanceState)
+ App1.setActivityTheme(this)
+ initUI()
+
+ app_state = App1.getAppState(this)
+ density = app_state.density
+ column_index = intent.getIntExtra(EXTRA_COLUMN_INDEX, 0)
+ column = app_state.column_list[column_index]
+
+ load(column.language_filter ?: JSONObject())
+ }
+
+ private fun initUI() {
+ setContentView(R.layout.act_language_filter)
+ App1.initEdgeToEdge(this)
+ Styler.fixHorizontalPadding(findViewById(R.id.llContent))
+
+ for(id in intArrayOf(
+ R.id.btnAdd,
+ R.id.btnSave,
+ R.id.btnMore
+ )) {
+ findViewById(id)?.setOnClickListener(this)
+ }
+
+ listView = findViewById(R.id.listView)
+ adapter = MyAdapter()
+ listView.adapter = adapter
+ listView.onItemClickListener = adapter
+ }
+
+ private fun load(src : JSONObject) {
+ loading_busy = true
+ try {
+
+ languageList.clear()
+ for(key in src.keys()) {
+ languageList.add(MyItem(key, src.parseBoolean(key) ?: true))
+ }
+ if(null == languageList.find { it.code == TootStatus.LANGUAGE_CODE_DEFAULT }) {
+ languageList.add(MyItem(TootStatus.LANGUAGE_CODE_DEFAULT, true))
+ }
+
+ languageList.sortWith(languageComparator)
+ adapter.notifyDataSetChanged()
+ } finally {
+ loading_busy = false
+ }
+ }
+
+ private fun save() {
+ val dst = JSONObject()
+ for(item in languageList) {
+ dst.put(item.code, item.allow)
+ }
+ column.language_filter = dst
+ }
+
+ private inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
+
+ override fun getCount() : Int = languageList.size
+ override fun getItemId(idx : Int) : Long = 0L
+ override fun getItem(idx : Int) : Any = languageList[idx]
+
+ override fun getView(idx : Int, viewArg : View?, parent : ViewGroup?) : View {
+ val tv = (viewArg ?: layoutInflater.inflate(
+ R.layout.lv_language_filter,
+ parent,
+ false
+ )) as TextView
+ val item = languageList[idx]
+ tv.text = String.format(
+ "%s %s : %s",
+ item.code,
+ getDesc(item),
+ getString(if(item.allow) R.string.language_show else R.string.language_hide)
+ )
+ tv.textColor = getAttributeColor(
+ this@ActLanguageFilter, when(item.allow) {
+ true -> R.attr.colorContentText
+ false -> R.attr.colorRegexFilterError
+ }
+ )
+ return tv
+ }
+
+ override fun onItemClick(parent : AdapterView<*>?, viewArg : View?, idx : Int, id : Long) {
+ if(idx in languageList.indices) edit(languageList[idx])
+ }
+ }
+
+ override fun onClick(v : View) {
+ when(v.id) {
+ R.id.btnSave -> {
+ save()
+ val data = Intent()
+ data.putExtra(EXTRA_COLUMN_INDEX, column_index)
+ setResult(RESULT_OK, data)
+ finish()
+ }
+
+ R.id.btnAdd -> edit(null)
+
+ R.id.btnMore -> {
+ ActionsDialog()
+ .addAction(getString(R.string.clear_all)) {
+ languageList.clear()
+ languageList.add(MyItem(TootStatus.LANGUAGE_CODE_DEFAULT, true))
+ adapter.notifyDataSetChanged()
+ }
+ .addAction(getString(R.string.export)) { export() }
+ .addAction(getString(R.string.import_)) { import() }
+ .show(this)
+ }
+ }
+ }
+
+ private fun edit(myItem : MyItem?) =
+ DlgLanguageFilter.open(this, myItem, object : DlgLanguageFilter.Callback {
+ override fun onOK(code : String, allow : Boolean) {
+ val it = languageList.iterator()
+ while(it.hasNext()) {
+ val item = it.next()
+ if(item.code == code) {
+ item.allow = allow
+ adapter.notifyDataSetChanged()
+ return
+ }
+ }
+ languageList.add(MyItem(code, allow))
+ languageList.sortWith(languageComparator)
+ adapter.notifyDataSetChanged()
+ return
+ }
+
+ override fun onDelete(code : String) {
+ val it = languageList.iterator()
+ while(it.hasNext()) {
+ val item = it.next()
+ if(item.code == code) it.remove()
+ }
+ adapter.notifyDataSetChanged()
+ }
+ })
+
+ private object DlgLanguageFilter {
+
+ interface Callback {
+ fun onOK(code : String, allow : Boolean)
+ fun onDelete(code : String)
+ }
+
+ @SuppressLint("InflateParams")
+ fun open(activity : ActLanguageFilter, item : MyItem?, callback : Callback) {
+
+ val view = activity.layoutInflater.inflate(R.layout.dlg_language_filter, null, false)
+
+ val etLanguage : EditText = view.findViewById(R.id.etLanguage)
+ val btnPresets : ImageButton = view.findViewById(R.id.btnPresets)
+ val tvLanguage : TextView = view.findViewById(R.id.tvLanguage)
+
+ val rbShow : RadioButton = view.findViewById(R.id.rbShow)
+ val rbHide : RadioButton = view.findViewById(R.id.rbHide)
+
+ when(item?.allow ?: true) {
+ true -> rbShow.isChecked = true
+ else -> rbHide.isChecked = true
+ }
+
+ fun updateDesc() {
+ val code = etLanguage.text.toString().trim()
+ val desc = activity.languageNameMap[code] ?: activity.getString(R.string.custom)
+ tvLanguage.text = desc
+ }
+
+ val languageList =
+ activity.languageNameMap.map { MyItem(it.key, true) }.sortedWith(languageComparator)
+ btnPresets.setOnClickListener {
+ val ad = ActionsDialog()
+ for(a in languageList) {
+ ad.addAction("${a.code} ${activity.getDesc(a)}") {
+ etLanguage.setText(a.code)
+ updateDesc()
+ }
+ }
+ ad.show(activity, activity.getString(R.string.presets))
+ }
+
+ etLanguage.setText(item?.code ?: "")
+ updateDesc()
+
+ etLanguage.addTextChangedListener(object : TextWatcher {
+ override fun afterTextChanged(p0 : Editable?) {
+ updateDesc()
+ }
+
+ override fun beforeTextChanged(p0 : CharSequence?, p1 : Int, p2 : Int, p3 : Int) {
+ }
+
+ override fun onTextChanged(p0 : CharSequence?, p1 : Int, p2 : Int, p3 : Int) {
+ }
+
+ })
+ if(item != null) {
+ etLanguage.isEnabled = false
+ btnPresets.isEnabled = false
+ btnPresets.setEnabledColor(activity,R.drawable.ic_edit, getAttributeColor(activity,R.attr.colorVectorDrawable),false)
+ }
+
+ val builder = AlertDialog.Builder(activity)
+ .setView(view)
+ .setCancelable(true)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.ok) { _, _ ->
+ callback.onOK(etLanguage.text.toString().trim(), rbShow.isChecked)
+ }
+
+ if(item != null && item.code != TootStatus.LANGUAGE_CODE_DEFAULT) {
+ builder.setNeutralButton(R.string.delete) { _, _ ->
+ callback.onDelete(etLanguage.text.toString().trim())
+ }
+ }
+
+ builder.show()
+ }
+ }
+
+ private fun export() {
+
+ val progress = ProgressDialogEx(this)
+
+ val data = JSONObject().apply {
+ for(item in languageList) {
+ put(item.code, item.allow)
+ }
+ }
+ .toString()
+ .encodeUTF8()
+
+ val task = @SuppressLint("StaticFieldLeak")
+ object : AsyncTask() {
+
+ override fun doInBackground(vararg params : Void) : File? {
+
+ try {
+ val cache_dir = cacheDir
+ cache_dir.mkdir()
+
+ val file = File(
+ cache_dir,
+ "SubwayTooter-language-filter.${Process.myPid()}.${Process.myTid()}.json"
+ )
+ FileOutputStream(file).use { it.write(data) }
+ return file
+ } catch(ex : Throwable) {
+ log.trace(ex)
+ showToast(
+ this@ActLanguageFilter,
+ ex,
+ "can't save filter data to temporary file."
+ )
+ }
+
+ return null
+ }
+
+ override fun onCancelled(result : File?) {
+ onPostExecute(result)
+ }
+
+ override fun onPostExecute(result : File?) {
+ progress.dismissSafe()
+
+ if(isCancelled || result == null) {
+ // cancelled.
+ return
+ }
+
+ try {
+ val uri = FileProvider.getUriForFile(
+ this@ActLanguageFilter,
+ App1.FILE_PROVIDER_AUTHORITY,
+ result
+ )
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.type = contentResolver.getType(uri)
+ intent.putExtra(Intent.EXTRA_SUBJECT, "SubwayTooter language filter data")
+ intent.putExtra(Intent.EXTRA_STREAM, uri)
+
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ startActivityForResult(intent, REQUEST_CODE_OTHER)
+ } catch(ex : Throwable) {
+ log.trace(ex)
+ showToast(this@ActLanguageFilter, ex, "export failed.")
+ }
+
+ }
+ }
+
+ progress.isIndeterminateEx = true
+ progress.setCancelable(true)
+ progress.setOnCancelListener { task.cancel(true) }
+ progress.show()
+ task.executeOnExecutor(App1.task_executor)
+ }
+
+ private fun import() {
+ try {
+ val intent = intentOpenDocument("*/*")
+ startActivityForResult(intent, REQUEST_CODE_IMPORT)
+ } catch(ex : Throwable) {
+ showToast(this, ex, "import failed.")
+ }
+
+ }
+
+ override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
+ if(resultCode == RESULT_OK && data != null && requestCode == REQUEST_CODE_IMPORT) {
+ data.handleGetContentResult(contentResolver).firstOrNull()?.uri?.let {
+ import2(it)
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+
+ private fun import2(uri : Uri) {
+
+ val type = contentResolver.getType(uri)
+ log.d("import2 type=%s", type)
+
+ val progress = ProgressDialogEx(this)
+
+ val task = @SuppressLint("StaticFieldLeak")
+ object : AsyncTask() {
+
+ override fun doInBackground(vararg params : Void) : JSONObject? {
+ try {
+ val source = contentResolver.openInputStream(uri)
+ if(source == null) {
+ showToast(this@ActLanguageFilter, true, "openInputStream failed.")
+ return null
+ }
+ return source.use { inStream ->
+ val bao = ByteArrayOutputStream()
+ IOUtils.copy(inStream, bao)
+ JSONObject(bao.toByteArray().decodeUTF8())
+ }
+ } catch(ex : Throwable) {
+ log.trace(ex)
+ showToast(this@ActLanguageFilter, ex, "can't load filter data.")
+ return null
+ }
+ }
+
+ override fun onCancelled(result : JSONObject?) {
+ onPostExecute(result)
+ }
+
+ override fun onPostExecute(result : JSONObject?) {
+ progress.dismissSafe()
+
+ // cancelled.
+ if(isCancelled || result == null) return
+
+ load(result)
+ }
+ }
+
+ progress.isIndeterminateEx = true
+ progress.setCancelable(true)
+ progress.setOnCancelListener { task.cancel(true) }
+ progress.show()
+ task.executeOnExecutor(App1.task_executor)
+ }
+}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt
index 72ac6a39..0b747d9d 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt
@@ -91,6 +91,7 @@ class ActMain : AppCompatActivity()
const val REQUEST_CODE_COLUMN_COLOR = 6
const val REQUEST_CODE_APP_SETTING = 7
const val REQUEST_CODE_TEXT = 8
+ const val REQUEST_CODE_LANGUAGE_FILTER = 9
const val COLUMN_WIDTH_MIN_DP = 300
@@ -1018,7 +1019,7 @@ class ActMain : AppCompatActivity()
REQUEST_CODE_COLUMN_COLOR -> if(data != null) {
app_state.saveColumnList()
val idx = data.getIntExtra(ActColumnCustomize.EXTRA_COLUMN_INDEX, 0)
- if(idx >= 0 && idx < app_state.column_list.size) {
+ if(idx in app_state.column_list.indices ) {
app_state.column_list[idx].fireColumnColor()
app_state.column_list[idx].fireShowContent(
reason = "ActMain column color changed",
@@ -1027,6 +1028,14 @@ class ActMain : AppCompatActivity()
}
updateColumnStrip()
}
+
+ REQUEST_CODE_LANGUAGE_FILTER -> if(data != null) {
+ app_state.saveColumnList()
+ val idx = data.getIntExtra(ActLanguageFilter.EXTRA_COLUMN_INDEX, 0)
+ if(idx in app_state.column_list.indices ) {
+ app_state.column_list[idx].onLanguageFilterChanged()
+ }
+ }
}
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.kt b/app/src/main/java/jp/juggler/subwaytooter/Column.kt
index 11cd7020..4bf2a040 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/Column.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/Column.kt
@@ -162,6 +162,7 @@ class Column(
private const val KEY_QUICK_FILTER = "quickFilter"
private const val KEY_REGEX_TEXT = "regex_text"
+ private const val KEY_LANGUAGE_FILTER = "language_filter"
private const val KEY_HEADER_BACKGROUND_COLOR = "header_background_color"
private const val KEY_HEADER_TEXT_COLOR = "header_text_color"
@@ -487,6 +488,8 @@ class Column(
internal var hashtag_none : String = ""
internal var hashtag_acct : String = ""
+ internal var language_filter : JSONObject? = null
+
// プロフカラムでのアカウント情報
@Volatile
internal var who_account : TootAccountRef? = null
@@ -552,6 +555,7 @@ class Column(
|| dont_show_reply
|| dont_show_reaction
|| dont_show_vote
+ || (language_filter?.length()?:0) >0
)
@Volatile
@@ -705,6 +709,7 @@ class Column(
last_viewing_item_id = EntityId.from(src, KEY_LAST_VIEWING_ITEM)
regex_text = src.parseString(KEY_REGEX_TEXT) ?: ""
+ language_filter = src.optJSONObject(KEY_LANGUAGE_FILTER)
header_bg_color = src.optInt(KEY_HEADER_BACKGROUND_COLOR)
header_fg_color = src.optInt(KEY_HEADER_TEXT_COLOR)
@@ -807,6 +812,9 @@ class Column(
dst.put(KEY_REGEX_TEXT, regex_text)
+ val ov = language_filter
+ if( ov != null) dst.put(KEY_LANGUAGE_FILTER,ov)
+
dst.put(KEY_HEADER_BACKGROUND_COLOR, header_bg_color)
dst.put(KEY_HEADER_TEXT_COLOR, header_fg_color)
dst.put(KEY_COLUMN_BACKGROUND_COLOR, column_bg_color)
@@ -1314,6 +1322,10 @@ class Column(
}
+ fun onLanguageFilterChanged() {
+ // TODO
+ }
+
internal fun addColumnViewHolder(cvh : ColumnViewHolder) {
// 現在のリストにあるなら削除する
@@ -1451,28 +1463,37 @@ class Column(
if(isFilteredByAttachment(status)) return true
+ val reblog = status.reblog
+
if(dont_show_boost) {
- if(status.reblog != null) return true
+ if(reblog != null) return true
}
if(dont_show_reply) {
if(status.in_reply_to_id != null) return true
- if(status.reblog?.in_reply_to_id != null) return true
+ if(reblog?.in_reply_to_id != null) return true
}
if(dont_show_normal_toot) {
- if(status.in_reply_to_id == null && status.reblog == null) return true
+ if(status.in_reply_to_id == null && reblog == null) return true
}
if(column_regex_filter(status.decoded_content)) return true
- if(column_regex_filter(status.reblog?.decoded_content)) return true
+ if(column_regex_filter(reblog?.decoded_content)) return true
if(column_regex_filter(status.decoded_spoiler_text)) return true
- if(column_regex_filter(status.reblog?.decoded_spoiler_text)) return true
+ if(column_regex_filter(reblog?.decoded_spoiler_text)) return true
+
+ val languageFilter = language_filter
+ if(languageFilter != null ){
+ val bShow = languageFilter.parseBoolean(status.language ?: reblog?.language ?:TootStatus.LANGUAGE_CODE_UNKNOWN)
+ ?: languageFilter.parseBoolean(TootStatus.LANGUAGE_CODE_DEFAULT)
+ ?: true
+ if(!bShow) return true
+ }
if(access_info.isPseudo) {
var r = UserRelation.loadPseudo(access_info.getFullAcct(status.account))
if(r.muting || r.blocking) return true
- val reblog = status.reblog
if(reblog != null) {
r = UserRelation.loadPseudo(access_info.getFullAcct(reblog.account))
if(r.muting || r.blocking) return true
@@ -1535,7 +1556,16 @@ class Column(
// just update _filtered flag for reversible filter
status.updateKeywordFilteredFlag(access_info, filterTrees)
}
-
+ if( status != null){
+ val languageFilter = language_filter
+ if(languageFilter != null ){
+ val bShow = languageFilter.parseBoolean(status.language ?: status.reblog?.language ?:TootStatus.LANGUAGE_CODE_UNKNOWN)
+ ?: languageFilter.parseBoolean(TootStatus.LANGUAGE_CODE_DEFAULT)
+ ?: true
+ if(!bShow) return true
+ }
+ }
+
if(status?.checkMuted() == true) {
log.d("isFiltered: status muted by in-app muted words.")
return true
@@ -3002,6 +3032,7 @@ class Column(
getHeaderNameColor()
)
}
+
// fun findListIndexByTimelineId(orderId : EntityId) : Int? {
// list_data.forEachIndexed { i, v ->
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt
index c9036afa..3966d937 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt
@@ -113,6 +113,7 @@ class ColumnViewHolder(
private lateinit var llRegexFilter : View
private lateinit var btnDeleteNotification : Button
private lateinit var btnColor : Button
+ private lateinit var btnLanguageFilter : Button
private lateinit var svQuickFilter : HorizontalScrollView
private lateinit var btnQuickFilterAll : Button
@@ -309,6 +310,7 @@ class ColumnViewHolder(
btnDeleteNotification.setOnClickListener(this)
btnColor.setOnClickListener(this)
+ btnLanguageFilter.setOnClickListener(this)
refreshLayout.setOnRefreshListener(this)
refreshLayout.setDistanceToTriggerSync((0.5f + 20f * activity.density).toInt())
@@ -586,6 +588,7 @@ class ColumnViewHolder(
vg(cbWithHighlight, bAllowFilter)
vg(etRegexFilter, bAllowFilter)
vg(llRegexFilter, bAllowFilter)
+ vg(btnLanguageFilter,bAllowFilter)
vg(cbDontShowBoost, column.canFilterBoost())
vg(cbDontShowReply, column.canFilterReply())
@@ -1030,6 +1033,11 @@ class ColumnViewHolder(
ActColumnCustomize.open(activity, idx, ActMain.REQUEST_CODE_COLUMN_COLOR)
}
+ btnLanguageFilter ->{
+ val idx = activity.app_state.column_list.indexOf(column)
+ ActLanguageFilter.open(activity, idx, ActMain.REQUEST_CODE_LANGUAGE_FILTER)
+ }
+
btnListAdd -> {
val tv = etListName.text.toString().trim { it <= ' ' }
if(tv.isEmpty()) {
@@ -1871,6 +1879,12 @@ class ColumnViewHolder(
isAllCaps = false
text = context.getString(R.string.color_and_background)
}.lparams(matchParent, wrapContent)
+
+ btnLanguageFilter = button {
+ isAllCaps = false
+ text = context.getString(R.string.language_filter)
+ }.lparams(matchParent, wrapContent)
+
}
} // end of column setting scroll view
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
index 27e1cde3..b053a099 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootStatus.kt
@@ -93,7 +93,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
val sensitive : Boolean
// The detected language for the status, if detected
- private val language : String?
+ val language : String?
//If not empty, warning text that should be displayed before the actual content
// アプリ内部では空文字列はCWなしとして扱う
@@ -485,7 +485,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
parseItem(::TootApplication, parser, src.optJSONObject("application"), log)
this.pinned = parser.pinned || src.optBoolean("pinned")
this.muted = src.optBoolean("muted")
- this.language = src.parseString("language")
+ this.language = src.parseString("language")?.notEmpty()
this.decoded_mentions = HTMLDecoder.decodeMentions(
parser.linkHelper,
this.mentions,
@@ -815,6 +815,9 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
@Volatile
internal var muted_word : WordTrieTree? = null
+ const val LANGUAGE_CODE_UNKNOWN="unknown"
+ const val LANGUAGE_CODE_DEFAULT="default"
+
val EMPTY_SPANNABLE = SpannableString("")
// OStatus
diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirm.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirm.kt
index 9a3901a5..87882f51 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirm.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirm.kt
@@ -2,13 +2,11 @@ package jp.juggler.subwaytooter.dialog
import android.annotation.SuppressLint
import android.app.Activity
-import androidx.appcompat.app.AlertDialog
import android.view.View
import android.widget.CheckBox
import android.widget.TextView
-
+import androidx.appcompat.app.AlertDialog
import jp.juggler.subwaytooter.R
-import jp.juggler.subwaytooter.util.EmptyCallback
object DlgConfirm {
diff --git a/app/src/main/res/layout/act_language_filter.xml b/app/src/main/res/layout/act_language_filter.xml
new file mode 100644
index 00000000..dde7a36e
--- /dev/null
+++ b/app/src/main/res/layout/act_language_filter.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dlg_language_filter.xml b/app/src/main/res/layout/dlg_language_filter.xml
new file mode 100644
index 00000000..3a5140a8
--- /dev/null
+++ b/app/src/main/res/layout/dlg_language_filter.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/lv_language_filter.xml b/app/src/main/res/layout/lv_language_filter.xml
new file mode 100644
index 00000000..1bb89ebb
--- /dev/null
+++ b/app/src/main/res/layout/lv_language_filter.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/page_column.xml b/app/src/main/res/layout/page_column.xml
deleted file mode 100644
index 3c627148..00000000
--- a/app/src/main/res/layout/page_column.xml
+++ /dev/null
@@ -1,649 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index efbe4343..78937373 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -980,5 +980,19 @@
(fedibird)ドメインタイムライン
ドメインTL
%1$sのドメインTL
+ 言語フィルタ
+ エクスポート
+ インポート
+ (デフォルト)
+ (不明)
+ 表示する
+ 隠す
+ 言語
+ 表示する/隠す
+ フィルタを適用するにはカラムをリロードしてください
+ 全て消去
+ プリセット
+ カスタム
+ プリセットを選択または言語コードを入力
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c3424505..abb5e72a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -974,4 +974,18 @@
(fedibird)Domain timeline
Domain timeline
%1$s domain timeline
+ Language filter
+ Export
+ Import
+ (default)
+ (unknown)
+ Show
+ Hide
+ Language
+ Show/Hide
+ column reload required to apply filter.
+ Clear all
+ Presets
+ Custom
+ choose preset or input language code
\ No newline at end of file