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
|
minSdkVersion min_sdk_version
|
||||||
|
|
||||||
versionCode 402
|
versionCode 402
|
||||||
versionName "4.0.2"
|
versionName "4.0.2x"
|
||||||
applicationId "jp.juggler.subwaytooter"
|
applicationId "jp.juggler.subwaytooter"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.PersistableBundle
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
@ -38,6 +39,7 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
||||||
internal val log = LogCategory("ActLanguageFilter")
|
internal val log = LogCategory("ActLanguageFilter")
|
||||||
|
|
||||||
internal const val EXTRA_COLUMN_INDEX = "column_index"
|
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) {
|
fun open(activity : ActMain, idx : Int, request_code : Int) {
|
||||||
val intent = Intent(activity, ActLanguageFilter::class.java)
|
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 {
|
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_DEFAULT, getString(R.string.language_code_default))
|
||||||
put(TootStatus.LANGUAGE_CODE_UNKNOWN, getString(R.string.language_code_unknown))
|
put(TootStatus.LANGUAGE_CODE_UNKNOWN, getString(R.string.language_code_unknown))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDesc(item : MyItem) : String {
|
private fun getDesc(item : MyItem) : String {
|
||||||
|
@ -121,9 +137,39 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
||||||
column_index = intent.getIntExtra(EXTRA_COLUMN_INDEX, 0)
|
column_index = intent.getIntExtra(EXTRA_COLUMN_INDEX, 0)
|
||||||
column = app_state.column_list[column_index]
|
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)
|
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() {
|
private fun initUI() {
|
||||||
setContentView(R.layout.act_language_filter)
|
setContentView(R.layout.act_language_filter)
|
||||||
App1.initEdgeToEdge(this)
|
App1.initEdgeToEdge(this)
|
||||||
|
@ -143,21 +189,30 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
||||||
listView.onItemClickListener = adapter
|
listView.onItemClickListener = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UIのデータをJsonObjectにエンコード
|
||||||
|
private fun encodeLanguageList() = jsonObject {
|
||||||
|
for(item in languageList) {
|
||||||
|
put(item.code, item.allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun load(src : JsonObject?) {
|
private fun load(src : JsonObject?) {
|
||||||
loading_busy = true
|
loading_busy = true
|
||||||
try {
|
try {
|
||||||
|
|
||||||
languageList.clear()
|
languageList.clear()
|
||||||
|
|
||||||
if(src != null) {
|
if(src != null) {
|
||||||
for(key in src.keys) {
|
for(key in src.keys) {
|
||||||
languageList.add(MyItem(key, src.boolean(key) ?: true))
|
languageList.add(MyItem(key, src.boolean(key) ?: true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(null == languageList.find { it.code == TootStatus.LANGUAGE_CODE_DEFAULT }) {
|
if(null == languageList.find { it.code == TootStatus.LANGUAGE_CODE_DEFAULT }) {
|
||||||
languageList.add(MyItem(TootStatus.LANGUAGE_CODE_DEFAULT, true))
|
languageList.add(MyItem(TootStatus.LANGUAGE_CODE_DEFAULT, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
languageList.sortWith(languageComparator)
|
languageList.sortWith(languageComparator)
|
||||||
|
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
} finally {
|
} finally {
|
||||||
loading_busy = false
|
loading_busy = false
|
||||||
|
@ -165,11 +220,7 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun save() {
|
private fun save() {
|
||||||
column.language_filter = jsonObject {
|
column.language_filter = encodeLanguageList()
|
||||||
for(item in languageList) {
|
|
||||||
put(item.code, item.allow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
|
private inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
|
||||||
|
|
|
@ -41,82 +41,76 @@ object AppDataExporter {
|
||||||
private const val KEY_HIGHLIGHT_WORD = "highlight_word"
|
private const val KEY_HIGHLIGHT_WORD = "highlight_word"
|
||||||
|
|
||||||
@Throws(IOException::class, JsonException::class)
|
@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()
|
writer.beginObject()
|
||||||
val it = src.keys.iterator()
|
for( entry in src.entries){
|
||||||
while(it.hasNext()) {
|
writer.name(entry.key)
|
||||||
val k = it.next()
|
writeJsonValue(writer, entry.value)
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer.endObject()
|
writer.endObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JsonException::class)
|
||||||
|
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)
|
@Throws(IOException::class, JsonException::class)
|
||||||
private fun readJsonObject(reader:JsonReader):JsonObject{
|
private fun readJsonObject(reader:JsonReader):JsonObject{
|
||||||
val dst = JsonObject()
|
val dst = JsonObject()
|
||||||
|
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
while(reader.hasNext()) {
|
while(reader.hasNext()) {
|
||||||
val name = reader.nextName()
|
val name = reader.nextName()
|
||||||
when(val token = reader.peek()) {
|
val value = readJsonValue(reader)
|
||||||
|
dst[name]=value
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
reader.endObject()
|
reader.endObject()
|
||||||
|
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +323,7 @@ object AppDataExporter {
|
||||||
for(column in app_state.column_list) {
|
for(column in app_state.column_list) {
|
||||||
val dst = JsonObject()
|
val dst = JsonObject()
|
||||||
column.encodeJSON(dst, 0)
|
column.encodeJSON(dst, 0)
|
||||||
writeJSONObject(writer, dst)
|
writeJsonObject(writer, dst)
|
||||||
}
|
}
|
||||||
writer.endArray()
|
writer.endArray()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.Pref
|
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(
|
private fun formatDate(
|
||||||
t : Long,
|
t : Long,
|
||||||
format:SimpleDateFormat ,
|
format:SimpleDateFormat ,
|
||||||
omitZeroSecond:Boolean
|
omitZeroSecond:Boolean,
|
||||||
|
omitYear:Boolean
|
||||||
) : String {
|
) : String {
|
||||||
var dateTarget = format.format(Date(t))
|
var dateTarget = format.format(Date(t))
|
||||||
|
|
||||||
|
@ -1071,11 +1072,16 @@ class TootStatus(parser : TootParser, src : JsonObject) : TimelineItem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 年の部分が現在と同じなら省略する
|
// 年の部分が現在と同じなら省略する
|
||||||
|
if(omitYear) {
|
||||||
val dateNow = format.format(Date())
|
val dateNow = format.format(Date())
|
||||||
val delm = dateNow.indexOf('-')
|
val delm = dateNow.indexOf('-')
|
||||||
if(delm!=-1 && dateNow.substring(0,delm+1) == dateTarget.substring(0,delm+1)){
|
if(delm != - 1 && dateNow.substring(0, delm + 1) == dateTarget.substring(
|
||||||
|
0,
|
||||||
|
delm + 1
|
||||||
|
)) {
|
||||||
dateTarget = dateTarget.substring(delm + 1)
|
dateTarget = dateTarget.substring(delm + 1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return dateTarget
|
return dateTarget
|
||||||
}
|
}
|
||||||
|
@ -1083,13 +1089,13 @@ class TootStatus(parser : TootParser, src : JsonObject) : TimelineItem() {
|
||||||
fun formatTimeRange(start : Long, end : Long, allDay : Boolean):Pair<String,String>{
|
fun formatTimeRange(start : Long, end : Long, allDay : Boolean):Pair<String,String>{
|
||||||
val strStart = when {
|
val strStart = when {
|
||||||
start <= 0L -> ""
|
start <= 0L -> ""
|
||||||
allDay-> formatDate(start,date_format2,omitZeroSecond = false)
|
allDay-> formatDate(start,date_format2,omitZeroSecond = false,omitYear = true)
|
||||||
else -> formatDate(start, date_format,omitZeroSecond = true)
|
else -> formatDate(start, date_format,omitZeroSecond = true,omitYear = true)
|
||||||
}
|
}
|
||||||
val strEnd = when {
|
val strEnd = when {
|
||||||
end <= 0L -> ""
|
end <= 0L -> ""
|
||||||
allDay-> formatDate(end,date_format2,omitZeroSecond = false)
|
allDay-> formatDate(end,date_format2,omitZeroSecond = false,omitYear = true)
|
||||||
else -> formatDate(end, date_format,omitZeroSecond = true)
|
else -> formatDate(end, date_format,omitZeroSecond = true,omitYear = true)
|
||||||
}
|
}
|
||||||
// 終了日は先頭と同じ部分を省略する
|
// 終了日は先頭と同じ部分を省略する
|
||||||
var skip = 0
|
var skip = 0
|
||||||
|
|
|
@ -4,8 +4,6 @@ import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.ResolveInfo
|
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -13,7 +11,6 @@ import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.action.CustomShare
|
import jp.juggler.subwaytooter.action.CustomShare
|
||||||
import jp.juggler.subwaytooter.action.cn
|
import jp.juggler.subwaytooter.action.cn
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
|
|
@ -1011,5 +1011,6 @@
|
||||||
<string name="emojione_shortcode_support_desc">サーバが古い間に作られた投稿は(サーバがアップデートした後でも)EmojiOneのショートコードを含むかもしれません。</string>
|
<string name="emojione_shortcode_support_desc">サーバが古い間に作られた投稿は(サーバがアップデートした後でも)EmojiOneのショートコードを含むかもしれません。</string>
|
||||||
<string name="copy_to_clipboard">クリップボードにコピー</string>
|
<string name="copy_to_clipboard">クリップボードにコピー</string>
|
||||||
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
||||||
|
<string name="language_filter_quit_waring">Language filter is not saved. exit anyway?</string>
|
||||||
|
|
||||||
</resources>
|
</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="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="copy_to_clipboard">Copy to clipboard</string>
|
||||||
<string name="copied_to_clipboard">Copied 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>
|
</resources>
|
Loading…
Reference in New Issue