diff --git a/app/build.gradle b/app/build.gradle index 47f494c..c3c7941 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "jp.ddo.hotmist.unicodepad" minSdkVersion 16 targetSdkVersion 32 - versionCode 52 - versionName "2.11.0" + versionCode 53 + versionName "2.12.0" } compileOptions { diff --git a/app/src/main/assets/namedb.zip b/app/src/main/assets/namedb.zip deleted file mode 100644 index 1386cec..0000000 Binary files a/app/src/main/assets/namedb.zip and /dev/null differ diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt index 2aea339..8b877fc 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt @@ -35,10 +35,10 @@ internal class CompleteAdapter(context: Context, pref: SharedPreferences) : Base private var current = "" fun update(str: String) { synchronized(lock) { - for (s in str.split(" ").toTypedArray()) if (s.isNotEmpty()) { + for (s in str.split(" ", "\n").toTypedArray()) if (s.isNotEmpty()) { (temp ?: list).let { it.remove(s) - if (it.size == 255) it.removeAt(254) + while (it.size >= 255) it.removeAt(254) it.add(0, s) } } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt index 82c56fd..787eb44 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt @@ -32,7 +32,7 @@ internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private private lateinit var grp: MutableList private lateinit var idx: MutableList private var current = pref.getInt("emoji", 0) - private var modifier = pref.getBoolean("modifier", true) + private var tone = pref.getInt("tone", 11034) private var guard = 0 private val scrollListener = object : AbsListView.OnScrollListener { override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {} @@ -78,14 +78,59 @@ internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private val jump = Spinner(activity) this.jump = jump hl.addView(jump, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) - val modc = CheckBox(activity) - modc.setText(R.string.modifier) - modc.setPadding(0, 0, (activity.resources.displayMetrics.density * 8f).toInt(), 0) - hl.addView(modc, LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)) + hl.addView(Spinner(activity).apply { + val adp = ArrayAdapter(activity, android.R.layout.simple_spinner_item, listOf("⬚", "\uD83C\uDFFB", "\uD83C\uDFFC", "\uD83C\uDFFD", "\uD83C\uDFFE", "\uD83C\uDFFF")).apply { + setDropDownViewResource(R.layout.spinner_drop_down_item) + } + adapter = adp + setSelection(adp.getPosition(Character.toChars(tone).concatToString())) + onItemSelectedListener = object : OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, v: View?, position: Int, id: Long) { + val item = adp.getItem(position)!!.codePointAt(0) + if (tone == item) return + tone = item + ++guard + view.setOnScrollListener(null) + jump.onItemSelectedListener = null + jump.adapter = null + cur?.close() + cur = db.emoji(UnicodeActivity.univer, item) + map = TreeMap() + grp = ArrayList() + idx = ArrayList() + var last2 = "" + cur?.let { + it.moveToFirst() + while (!it.isAfterLast) { + val curr = it.getString(1) + " / " + it.getString(2) + if (curr == last2) { + it.moveToNext() + continue + } + last2 = curr + map[it.position] = map.size + grp.add(curr) + idx.add(it.position) + it.moveToNext() + } + } + if (current >= grp.size) current = grp.size - 1 + view.invalidateViews() + jump.adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, grp).also { + it.setDropDownViewResource(R.layout.spinner_drop_down_item) + } + jump.setSelection(current) + view.setSelection(idx[current]) + view.setOnScrollListener(scrollListener) + jump.onItemSelectedListener = selectListener + view.post { --guard } + } + override fun onNothingSelected(parent: AdapterView<*>?) {} + } }, LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)) if (Build.VERSION.SDK_INT >= 21) hl.setPadding(0, (activity.resources.displayMetrics.density * 8f).toInt(), 0, (activity.resources.displayMetrics.density * 8f).toInt()) layout.addView(hl, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) layout.addView(this.view, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) - cur = db.emoji(UnicodeActivity.univer, modifier) + cur = db.emoji(UnicodeActivity.univer, tone) map = TreeMap() grp = ArrayList() idx = ArrayList() @@ -113,46 +158,6 @@ internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private jump.setSelection(current) view.setOnScrollListener(scrollListener) jump.onItemSelectedListener = selectListener - modc.isChecked = modifier - modc.setOnCheckedChangeListener { _: CompoundButton, isChecked: Boolean -> - if (modifier == isChecked) return@setOnCheckedChangeListener - modifier = isChecked - ++guard - view.setOnScrollListener(null) - jump.onItemSelectedListener = null - jump.adapter = null - cur?.close() - cur = db.emoji(UnicodeActivity.univer, modifier) - map = TreeMap() - grp = ArrayList() - idx = ArrayList() - var last2 = "" - cur?.let { - it.moveToFirst() - while (!it.isAfterLast) { - val curr = it.getString(1) + " / " + it.getString(2) - if (curr == last2) { - it.moveToNext() - continue - } - last2 = curr - map[it.position] = map.size - grp.add(curr) - idx.add(it.position) - it.moveToNext() - } - } - if (current >= grp.size) current = grp.size - 1 - view.invalidateViews() - jump.adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, grp).also { - it.setDropDownViewResource(R.layout.spinner_drop_down_item) - } - jump.setSelection(current) - view.setSelection(idx[current]) - view.setOnScrollListener(scrollListener) - jump.onItemSelectedListener = selectListener - view.post { --guard } - } return layout } @@ -172,7 +177,7 @@ internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private override fun save(edit: SharedPreferences.Editor) { edit.putInt("emoji", current) - edit.putBoolean("modifier", modifier) + edit.putInt("tone", tone) } override fun getCount(): Int { diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt index 240706c..373f916 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt @@ -31,7 +31,7 @@ class NameDatabase(context: Context) { operator fun get(code: Int, column: String): String? { if (column == "name") { if (code in 0xE000..0xF8FF || code in 0xFFF80..0xFFFFD || code in 0x10FF80..0x10FFFD) return "Private Use" - if (code in 0x3400..0x4DBF || code in 0x4E00..0x9FFF || code in 0x20000..0x2A6DF || code in 0x2A700..0x2B738 || code in 0x2B740..0x2B81D || code in 0x2B820..0x2CEA1 || code in 0x2CEB0..0x2EBE0 || code in 0x30000..0x3134A) return "CJK Unified Ideograph" + if (code in 0x3400..0x4DBF || code in 0x4E00..0x9FFF || code in 0x20000..0x2A6DF || code in 0x2A700..0x2B738 || code in 0x2B740..0x2B81D || code in 0x2B820..0x2CEA1 || code in 0x2CEB0..0x2EBE0 || code in 0x30000..0x3134A || code in 0x31350 .. 0x323AF) return "CJK Unified Ideograph" if (code in 0xAC00..0xD7A3) return "Hangul Syllable" if (code in 0x17000..0x187F7) return "Tangut Character" } @@ -39,7 +39,7 @@ class NameDatabase(context: Context) { } operator fun get(code: String, column: String): String? { - return get("emoji_table", "'$code'", column) + return get("emoji_table2", "'$code'", column) } private operator fun get(table: String, code: String, column: String): String? { @@ -73,7 +73,7 @@ class NameDatabase(context: Context) { } fun getInt(code: String, column: String): Int { - return getInt("emoji_table", "'$code'", column) + return getInt("emoji_table2", "'$code'", column) } private fun getInt(table: String, code: String, column: String): Int { @@ -100,14 +100,14 @@ class NameDatabase(context: Context) { else -> version } return try { - db.rawQuery("SELECT id FROM name_table WHERE " + list.joinToString(" ") { "words LIKE '%$it%' AND " } + "version <= $version UNION ALL SELECT id FROM emoji_table WHERE " + list.joinToString(" ") { "name LIKE '%$it%' AND " } + "version <= $emojiVersion;", null) + db.rawQuery("SELECT id FROM name_table WHERE " + list.joinToString(" ") { "words LIKE '%$it%' AND " } + "version <= $version UNION ALL SELECT id FROM emoji_table2 WHERE " + list.joinToString(" ") { "name LIKE '%$it%' AND " } + "version <= $emojiVersion;", null) } catch (e: SQLiteException) { null } } @SuppressLint("Recycle") - fun emoji(version: Int, mod: Boolean): Cursor? { + fun emoji(version: Int, tone: Int): Cursor? { val emojiVersion = when (version) { 600, 610, 620, 630 -> 60 700 -> 70 @@ -115,7 +115,7 @@ class NameDatabase(context: Context) { else -> version } return try { - db.rawQuery("SELECT id, grp, subgrp FROM emoji_table WHERE version <= $emojiVersion" + if (mod) ";" else " AND mod = 0;", null) + db.rawQuery("SELECT id, grp, subgrp FROM emoji_table2 WHERE version <= $emojiVersion AND (tone = $tone OR tone = 0);", null) } catch (e: SQLiteException) { null } @@ -129,7 +129,7 @@ class NameDatabase(context: Context) { return try { val db = SQLiteDatabase.openDatabase(context.getFileStreamPath(dbpath).absolutePath, null, SQLiteDatabase.OPEN_READONLY or SQLiteDatabase.NO_LOCALIZED_COLLATORS) try { - db.rawQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='name_table' OR name='emoji_table';", null).use { cur -> + db.rawQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='name_table' OR name='emoji_table2';", null).use { cur -> cur.moveToFirst() if (cur.getInt(0) != 2) throw SQLiteException() } @@ -137,7 +137,7 @@ class NameDatabase(context: Context) { cur.moveToFirst() if (cur.getInt(0) != 34930) throw SQLiteException() } - db.rawQuery("SELECT COUNT(*) FROM 'emoji_table';", null).use { cur -> + db.rawQuery("SELECT COUNT(*) FROM 'emoji_table2';", null).use { cur -> cur.moveToFirst() if (cur.getInt(0) != 3655) throw SQLiteException() } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt index 727940d..1507b54 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt @@ -16,12 +16,14 @@ package jp.ddo.hotmist.unicodepad import android.annotation.SuppressLint +import android.app.AlertDialog import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration import android.graphics.Typeface import android.os.Build import android.os.Bundle +import android.os.Handler import android.os.Process import android.provider.OpenableColumns import android.text.* @@ -31,7 +33,6 @@ import android.view.inputmethod.InputMethodManager import android.widget.* import androidx.appcompat.app.AppCompatActivity import androidx.core.provider.FontRequest -import androidx.core.view.MenuItemCompat import androidx.core.view.doOnLayout import androidx.emoji2.text.EmojiCompat import androidx.emoji2.text.EmojiCompat.InitCallback @@ -43,6 +44,7 @@ import java.io.FileOutputStream import java.io.IOException import java.util.* import java.util.zip.CRC32 +import kotlin.concurrent.thread import kotlin.math.max import kotlin.math.min @@ -51,12 +53,14 @@ import kotlin.math.min class UnicodeActivity : AppCompatActivity() { private lateinit var editText: EditText private lateinit var btnClear: ImageButton - private lateinit var btnFinish: Button + private lateinit var btnFinish: MenuItem private lateinit var chooser: FontChooser private lateinit var locale: LocaleChooser private lateinit var scroll: LockableScrollView private lateinit var pager: ViewPager internal lateinit var adpPage: PageAdapter + private lateinit var itemUndo: MenuItem + private lateinit var itemRedo: MenuItem private val adCompat: AdCompat = AdCompatImpl() private lateinit var cm: ClipboardManager private lateinit var pref: SharedPreferences @@ -65,6 +69,8 @@ class UnicodeActivity : AppCompatActivity() { private var disableime = false private var delay: Runnable? = null private var timer = 500 + private val history = mutableListOf(Triple("", 0, 0)) + private var historyCursor = 0 @SuppressLint("ClickableViewAccessibility") public override fun onCreate(savedInstanceState: Bundle?) { pref = PreferenceManager.getDefaultSharedPreferences(this) @@ -99,13 +105,40 @@ class UnicodeActivity : AppCompatActivity() { true } it.textSize = fontsize - it.setOnEditorActionListener { _, _, keyEvent -> - if (keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER) { - if (keyEvent.action == KeyEvent.ACTION_DOWN) btnFinish.performClick() + it.setOnEditorActionListener { _, actionId, keyEvent -> + if (keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.action == KeyEvent.ACTION_DOWN || actionId == EditorInfo.IME_ACTION_DONE) { + onOptionsItemSelected(btnFinish) true } else false } + it.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + if (!::itemUndo.isInitialized) { + history[0] = Triple(s.toString(), 0, 0) + return + } + if (s.toString() == history[historyCursor].first) { + return + } + while (history.size > historyCursor + 1) { + history.removeLast() + } + while (history.size >= MAX_HISTORY) { + history.removeFirst() + } + history.add(Triple(s.toString(), it.selectionStart, it.selectionEnd)) + historyCursor = history.size - 1 + itemUndo.isEnabled = historyCursor > 0 + itemRedo.isEnabled = false + } + }) } btnClear = findViewById(R.id.clear).also { it.setOnClickListener { @@ -124,7 +157,6 @@ class UnicodeActivity : AppCompatActivity() { val start = editText.selectionStart if (start < 1) return@Runnable val end = editText.selectionEnd - if (start < 1) return@Runnable if (start != end) editText.editableText.delete(min(start, end), max(start, end)) else if (start > 1 && Character.isSurrogatePair(str[start - 2], str[start - 1])) editText.editableText.delete(start - 2, start) else editText.editableText.delete(start - 1, start) if (delay != null) { editText.postDelayed(delay, timer.toLong()) @@ -142,53 +174,6 @@ class UnicodeActivity : AppCompatActivity() { true } } - findViewById