fix #129. add confirmation when quit language filter editor without save. fix export/import error when language filter is set.
This commit is contained in:
parent
947fab0340
commit
74933a1491
|
@ -22,7 +22,7 @@ android {
|
|||
minSdkVersion min_sdk_version
|
||||
|
||||
versionCode 402
|
||||
versionName "4.0.2"
|
||||
versionName "4.0.2x"
|
||||
applicationId "jp.juggler.subwaytooter"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
|||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import android.os.Process
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
|
@ -38,6 +39,7 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
internal val log = LogCategory("ActLanguageFilter")
|
||||
|
||||
internal const val EXTRA_COLUMN_INDEX = "column_index"
|
||||
private const val STATE_LANGUAGE_LIST = "language_list"
|
||||
|
||||
fun open(activity : ActMain, idx : Int, request_code : Int) {
|
||||
val intent = Intent(activity, ActLanguageFilter::class.java)
|
||||
|
@ -59,6 +61,19 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun equalsLanguageList(a : JsonObject?, b : JsonObject?) : Boolean {
|
||||
fun JsonObject.encodeToString() : String {
|
||||
val clone = this.toString().decodeJsonObject()
|
||||
if(! clone.contains(TootStatus.LANGUAGE_CODE_DEFAULT)) {
|
||||
clone[TootStatus.LANGUAGE_CODE_DEFAULT] = true
|
||||
}
|
||||
return clone.keys.sorted().joinToString(",") { "$it=${this[it]}" }
|
||||
}
|
||||
|
||||
val a_sign = (a ?: JsonObject()).encodeToString()
|
||||
val b_sign = (b ?: JsonObject()).encodeToString()
|
||||
return a_sign == b_sign
|
||||
}
|
||||
}
|
||||
|
||||
private val languageNameMap by lazy {
|
||||
|
@ -94,6 +109,7 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
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 {
|
||||
|
@ -121,9 +137,39 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
column_index = intent.getIntExtra(EXTRA_COLUMN_INDEX, 0)
|
||||
column = app_state.column_list[column_index]
|
||||
|
||||
if(savedInstanceState != null) {
|
||||
try {
|
||||
val sv = savedInstanceState.getString(STATE_LANGUAGE_LIST, null)
|
||||
if(sv != null) {
|
||||
val list = sv.decodeJsonObject()
|
||||
load(list)
|
||||
return
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
load(column.language_filter)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState : Bundle, outPersistentState : PersistableBundle) {
|
||||
super.onSaveInstanceState(outState, outPersistentState)
|
||||
outState.putString(STATE_LANGUAGE_LIST, encodeLanguageList().toString())
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if(! equalsLanguageList(column.language_filter, encodeLanguageList())) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(R.string.language_filter_quit_waring)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> finish() }
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
private fun initUI() {
|
||||
setContentView(R.layout.act_language_filter)
|
||||
App1.initEdgeToEdge(this)
|
||||
|
@ -143,21 +189,30 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
listView.onItemClickListener = adapter
|
||||
}
|
||||
|
||||
// UIのデータをJsonObjectにエンコード
|
||||
private fun encodeLanguageList() = jsonObject {
|
||||
for(item in languageList) {
|
||||
put(item.code, item.allow)
|
||||
}
|
||||
}
|
||||
|
||||
private fun load(src : JsonObject?) {
|
||||
loading_busy = true
|
||||
try {
|
||||
|
||||
languageList.clear()
|
||||
|
||||
if(src != null) {
|
||||
for(key in src.keys) {
|
||||
languageList.add(MyItem(key, src.boolean(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
|
||||
|
@ -165,11 +220,7 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
|
||||
private fun save() {
|
||||
column.language_filter = jsonObject {
|
||||
for(item in languageList) {
|
||||
put(item.code, item.allow)
|
||||
}
|
||||
}
|
||||
column.language_filter = encodeLanguageList()
|
||||
}
|
||||
|
||||
private inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
|
||||
|
|
|
@ -41,82 +41,76 @@ object AppDataExporter {
|
|||
private const val KEY_HIGHLIGHT_WORD = "highlight_word"
|
||||
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun writeJSONObject(writer : JsonWriter, src : JsonObject) {
|
||||
private fun writeJsonValue(writer : JsonWriter, value:Any?) {
|
||||
when(value) {
|
||||
null -> writer.nullValue()
|
||||
is String -> writer.value(value)
|
||||
is Boolean -> writer.value(value)
|
||||
is Number -> writer.value(value)
|
||||
is EntityId -> writer.value(value.toString())
|
||||
is JsonObject -> writeJsonObject(writer,value)
|
||||
is JsonArray ->writeJsonArray(writer,value)
|
||||
else -> throw RuntimeException("writeJsonValue: bad data type: $value")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun writeJsonArray(writer : JsonWriter, src : JsonArray) {
|
||||
writer.beginArray()
|
||||
for( value in src) {
|
||||
writeJsonValue(writer,value)
|
||||
}
|
||||
writer.endArray()
|
||||
}
|
||||
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun writeJsonObject(writer : JsonWriter, src : JsonObject) {
|
||||
writer.beginObject()
|
||||
val it = src.keys.iterator()
|
||||
while(it.hasNext()) {
|
||||
val k = it.next()
|
||||
if(src.isNull(k)) {
|
||||
writer.name(k)
|
||||
writer.nullValue()
|
||||
} else {
|
||||
when(val o = src[k]) {
|
||||
is String -> {
|
||||
writer.name(k)
|
||||
writer.value(o)
|
||||
|
||||
}
|
||||
|
||||
is Boolean -> {
|
||||
writer.name(k)
|
||||
writer.value(o)
|
||||
|
||||
}
|
||||
|
||||
is Number -> {
|
||||
|
||||
writer.name(k)
|
||||
writer.value(o)
|
||||
|
||||
}
|
||||
|
||||
is EntityId -> {
|
||||
writer.name(k)
|
||||
writer.value(o.toString())
|
||||
}
|
||||
|
||||
else -> throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"bad data type: JsonObject key =%s",
|
||||
k
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
for( entry in src.entries){
|
||||
writer.name(entry.key)
|
||||
writeJsonValue(writer, entry.value)
|
||||
}
|
||||
writer.endObject()
|
||||
}
|
||||
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun readJsonObject(reader : JsonReader) : JsonObject {
|
||||
val dst = JsonObject()
|
||||
private fun readJsonValue(reader:JsonReader):Any?{
|
||||
|
||||
return when(val token = reader.peek()) {
|
||||
JsonToken.NULL -> {
|
||||
reader.nextNull()
|
||||
null
|
||||
}
|
||||
|
||||
JsonToken.STRING -> reader.nextString()
|
||||
JsonToken.BOOLEAN -> reader.nextBoolean()
|
||||
JsonToken.NUMBER -> reader.nextDouble()
|
||||
JsonToken.BEGIN_ARRAY -> readJsonArray(reader)
|
||||
JsonToken.BEGIN_OBJECT -> readJsonObject(reader)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun readJsonArray(reader:JsonReader):JsonArray{
|
||||
val dst = JsonArray()
|
||||
reader.beginArray()
|
||||
while(reader.hasNext()) {
|
||||
dst.add(readJsonValue(reader))
|
||||
}
|
||||
reader.endArray()
|
||||
return dst
|
||||
}
|
||||
@Throws(IOException::class, JsonException::class)
|
||||
private fun readJsonObject(reader:JsonReader):JsonObject{
|
||||
val dst = JsonObject()
|
||||
reader.beginObject()
|
||||
while(reader.hasNext()) {
|
||||
val name = reader.nextName()
|
||||
when(val token = reader.peek()) {
|
||||
|
||||
JsonToken.NULL -> reader.nextNull()
|
||||
|
||||
JsonToken.STRING -> dst[name] = reader.nextString()
|
||||
|
||||
JsonToken.BOOLEAN -> dst[name] = reader.nextBoolean()
|
||||
|
||||
JsonToken.NUMBER -> dst[name] = reader.nextDouble()
|
||||
|
||||
else -> throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"bad data type: %s key =%s",
|
||||
token,
|
||||
name
|
||||
)
|
||||
)
|
||||
}
|
||||
val value = readJsonValue(reader)
|
||||
dst[name]=value
|
||||
}
|
||||
reader.endObject()
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
|
@ -329,7 +323,7 @@ object AppDataExporter {
|
|||
for(column in app_state.column_list) {
|
||||
val dst = JsonObject()
|
||||
column.encodeJSON(dst, 0)
|
||||
writeJSONObject(writer, dst)
|
||||
writeJsonObject(writer, dst)
|
||||
}
|
||||
writer.endArray()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
|
|
|
@ -1054,14 +1054,15 @@ class TootStatus(parser : TootParser, src : JsonObject) : TimelineItem() {
|
|||
}
|
||||
}
|
||||
|
||||
return formatDate(t,date_format,omitZeroSecond = false)
|
||||
return formatDate(t,date_format,omitZeroSecond = false,omitYear = false)
|
||||
}
|
||||
|
||||
// 告知の開始/終了日付
|
||||
private fun formatDate(
|
||||
t : Long,
|
||||
format:SimpleDateFormat ,
|
||||
omitZeroSecond:Boolean
|
||||
omitZeroSecond:Boolean,
|
||||
omitYear:Boolean
|
||||
) : String {
|
||||
var dateTarget = format.format(Date(t))
|
||||
|
||||
|
@ -1071,10 +1072,15 @@ class TootStatus(parser : TootParser, src : JsonObject) : TimelineItem() {
|
|||
}
|
||||
|
||||
// 年の部分が現在と同じなら省略する
|
||||
val dateNow = format.format(Date())
|
||||
val delm = dateNow.indexOf('-')
|
||||
if(delm!=-1 && dateNow.substring(0,delm+1) == dateTarget.substring(0,delm+1)){
|
||||
dateTarget = dateTarget.substring(delm+1)
|
||||
if(omitYear) {
|
||||
val dateNow = format.format(Date())
|
||||
val delm = dateNow.indexOf('-')
|
||||
if(delm != - 1 && dateNow.substring(0, delm + 1) == dateTarget.substring(
|
||||
0,
|
||||
delm + 1
|
||||
)) {
|
||||
dateTarget = dateTarget.substring(delm + 1)
|
||||
}
|
||||
}
|
||||
|
||||
return dateTarget
|
||||
|
@ -1083,13 +1089,13 @@ class TootStatus(parser : TootParser, src : JsonObject) : TimelineItem() {
|
|||
fun formatTimeRange(start : Long, end : Long, allDay : Boolean):Pair<String,String>{
|
||||
val strStart = when {
|
||||
start <= 0L -> ""
|
||||
allDay-> formatDate(start,date_format2,omitZeroSecond = false)
|
||||
else -> formatDate(start, date_format,omitZeroSecond = true)
|
||||
allDay-> formatDate(start,date_format2,omitZeroSecond = false,omitYear = true)
|
||||
else -> formatDate(start, date_format,omitZeroSecond = true,omitYear = true)
|
||||
}
|
||||
val strEnd = when {
|
||||
end <= 0L -> ""
|
||||
allDay-> formatDate(end,date_format2,omitZeroSecond = false)
|
||||
else -> formatDate(end, date_format,omitZeroSecond = true)
|
||||
allDay-> formatDate(end,date_format2,omitZeroSecond = false,omitYear = true)
|
||||
else -> formatDate(end, date_format,omitZeroSecond = true,omitYear = true)
|
||||
}
|
||||
// 終了日は先頭と同じ部分を省略する
|
||||
var skip = 0
|
||||
|
|
|
@ -4,8 +4,6 @@ import android.annotation.SuppressLint
|
|||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
|
@ -13,7 +11,6 @@ import android.view.ViewGroup
|
|||
import android.view.WindowManager
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.action.CustomShare
|
||||
import jp.juggler.subwaytooter.action.cn
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical"
|
||||
|
|
|
@ -1011,5 +1011,6 @@
|
|||
<string name="emojione_shortcode_support_desc">サーバが古い間に作られた投稿は(サーバがアップデートした後でも)EmojiOneのショートコードを含むかもしれません。</string>
|
||||
<string name="copy_to_clipboard">クリップボードにコピー</string>
|
||||
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
||||
<string name="language_filter_quit_waring">Language filter is not saved. exit anyway?</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1006,4 +1006,5 @@
|
|||
<string name="emojione_shortcode_support_desc">The toots made by old Mastodon may contain emojione\'s shortcode even if the server have been updated.</string>
|
||||
<string name="copy_to_clipboard">Copy to clipboard</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard.</string>
|
||||
<string name="language_filter_quit_waring">Language filter is not saved. exit anyway?</string>
|
||||
</resources>
|
Loading…
Reference in New Issue