From 74933a14917e32c2adfc9157c3321e40791c80ec Mon Sep 17 00:00:00 2001 From: tateisu Date: Sat, 1 Feb 2020 02:37:39 +0900 Subject: [PATCH] fix #129. add confirmation when quit language filter editor without save. fix export/import error when language filter is set. --- app/build.gradle | 2 +- .../juggler/subwaytooter/ActLanguageFilter.kt | 63 ++++++++- .../juggler/subwaytooter/AppDataExporter.kt | 124 +++++++++--------- .../subwaytooter/action/CustomShare.kt | 2 - .../subwaytooter/api/entity/TootStatus.kt | 26 ++-- .../subwaytooter/dialog/DlgAppPicker.kt | 3 - app/src/main/res/layout/lv_app_picker.xml | 2 +- app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 136 insertions(+), 88 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e68f83a6..5be5e06c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActLanguageFilter.kt b/app/src/main/java/jp/juggler/subwaytooter/ActLanguageFilter.kt index 452fd86f..b48b012b 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActLanguageFilter.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActLanguageFilter.kt @@ -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 { diff --git a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.kt b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.kt index 6d37dc60..8697b00e 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/AppDataExporter.kt @@ -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() } diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/CustomShare.kt b/app/src/main/java/jp/juggler/subwaytooter/action/CustomShare.kt index 973f3ba2..03c6612c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/CustomShare.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/CustomShare.kt @@ -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 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 12c1594c..56c46ddd 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 @@ -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{ 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 diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgAppPicker.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgAppPicker.kt index a688d703..e6ca6af9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgAppPicker.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgAppPicker.kt @@ -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 diff --git a/app/src/main/res/layout/lv_app_picker.xml b/app/src/main/res/layout/lv_app_picker.xml index c2a90604..fb9d9627 100644 --- a/app/src/main/res/layout/lv_app_picker.xml +++ b/app/src/main/res/layout/lv_app_picker.xml @@ -1,7 +1,7 @@ サーバが古い間に作られた投稿は(サーバがアップデートした後でも)EmojiOneのショートコードを含むかもしれません。 クリップボードにコピー クリップボードにコピーしました + Language filter is not saved. exit anyway? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5aefaf20..4ab39d5b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1006,4 +1006,5 @@ The toots made by old Mastodon may contain emojione\'s shortcode even if the server have been updated. Copy to clipboard Copied to clipboard. + Language filter is not saved. exit anyway? \ No newline at end of file