diff --git a/app/build.gradle b/app/build.gradle index c5760fb..50455f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "jp.ddo.hotmist.unicodepad" minSdkVersion 19 targetSdkVersion 34 - versionCode 57 - versionName "2.13.1" + versionCode 59 + versionName "2.13.3" } compileOptions { @@ -25,6 +25,7 @@ android { release { shrinkResources true minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -54,7 +55,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.emoji2:emoji2:1.4.0' implementation 'androidx.preference:preference-ktx:1.2.1' - implementation 'com.mobeta.android.dslv:library:0.9.0' + implementation 'com.github.woxthebox:draglistview:1.7.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'com.github.mreram:showcaseview:1.4.1' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..3f8a24c --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1 @@ +-keep class com.woxthebox.draglistview.** { *; } diff --git a/app/src/main/assets/namedb.zip b/app/src/main/assets/namedb.zip index 6557e33..8289d55 100644 Binary files a/app/src/main/assets/namedb.zip and b/app/src/main/assets/namedb.zip differ diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/BaseActivity.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/BaseActivity.kt new file mode 100644 index 0000000..e0e9d96 --- /dev/null +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/BaseActivity.kt @@ -0,0 +1,32 @@ +package jp.ddo.hotmist.unicodepad + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.PreferenceManager + +private val THEME = intArrayOf( + R.style.Theme, + R.style.Theme_Light, + R.style.Theme_Light_DarkActionBar, + R.style.Theme_DayNight +) + +abstract class BaseActivity : AppCompatActivity() { + private var currentTheme = -1 + private fun getThemeFromPref(): Int { + val pref = PreferenceManager.getDefaultSharedPreferences(this) + return THEME[(pref.getString("theme", null)?.toIntOrNull() ?: 2131492983) - 2131492983] + } + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(getThemeFromPref().also { currentTheme = it }) + super.onCreate(savedInstanceState) + } + + override fun onResume() { + super.onResume() + if (currentTheme != getThemeFromPref()) { + recreate() + } + } +} \ No newline at end of file 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 8b877fc..0d2a91b 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/CompleteAdapter.kt @@ -25,6 +25,7 @@ import android.widget.Filter import android.widget.Filterable import android.widget.TextView import java.util.* +import kotlin.math.min internal class CompleteAdapter(context: Context, pref: SharedPreferences) : BaseAdapter(), Filterable { private val lock = Any() @@ -48,7 +49,7 @@ internal class CompleteAdapter(context: Context, pref: SharedPreferences) : Base fun save(edit: SharedPreferences.Editor) { var str = "" - for (s in list) str += """ + for (s in list.subList(0, min(list.size, 255))) str += """ $s """.trimIndent() @@ -60,7 +61,7 @@ internal class CompleteAdapter(context: Context, pref: SharedPreferences) : Base } override fun getItem(position: Int): Any { - return current + list[position] + " " + return current + list[position] } override fun getItemId(position: Int): Long { diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/EditAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/EditAdapter.kt index efa08d4..d35d87d 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/EditAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/EditAdapter.kt @@ -16,28 +16,46 @@ package jp.ddo.hotmist.unicodepad import android.app.Activity +import android.os.Build import android.text.Editable import android.text.TextWatcher import android.view.View -import android.widget.AbsListView import android.widget.EditText -import com.mobeta.android.dslv.DragSortListView.DropListener -import com.mobeta.android.dslv.DragSortListView.RemoveListener import java.util.* +import kotlin.streams.toList -internal class EditAdapter(activity: Activity, db: NameDatabase, single: Boolean, private val edit: EditText) : UnicodeAdapter(activity, db, single), TextWatcher, DropListener, RemoveListener { - private val list: ArrayList = ArrayList() +internal class EditAdapter(activity: Activity, db: NameDatabase, single: Boolean, private val edit: EditText) : DragListUnicodeAdapter(activity, db, single), TextWatcher { private var suspend = false - override fun instantiate(view: View): View { - val view = view as AbsListView - list.clear() - val str = edit.editableText.toString() - var i = 0 - while (i < str.length) { - val code = str.codePointAt(i) - list.add(code) - i += Character.charCount(code) + private var sequence = 0L + + private fun setString(s: CharSequence) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mItemList = s.codePoints().mapToLong { it.toLong() or (sequence++ shl 32) }.toList().toMutableList() + } else { + mItemList = ArrayList() + for (i in s.indices) { + if (Character.isLowSurrogate(s[i])) continue + mItemList.add(Character.codePointAt(s, i).toLong() or (sequence++ shl 32)) + } + } + notifyDataSetChanged() + } + + private fun isSameString(s: CharSequence): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mItemList.map { it.toInt() } == s.codePoints().toList() + } else { + val list = ArrayList() + for (i in s.indices) { + if (Character.isLowSurrogate(s[i])) continue + list.add(Character.codePointAt(s, i)) + } + mItemList.map { it.toInt() } == list } + } + + override fun instantiate(view: View): View { + setString(edit.editableText) edit.addTextChangedListener(this) return super.instantiate(view) } @@ -49,13 +67,44 @@ internal class EditAdapter(activity: Activity, db: NameDatabase, single: Boolean fun updateString() { if (suspend) return - val str = edit.editableText.toString() - list.clear() - var i = 0 - while (i < str.length) { - val code = str.codePointAt(i) - list.add(code) - i += Character.charCount(code) + setString(edit.editableText) + } + + override fun getUniqueItemId(position: Int): Long { + return mItemList[position].toLong() + } + + override fun onItemDragStarted(position: Int) { + } + + override fun onItemDragging(itemPosition: Int, x: Float, y: Float) { + } + + override fun onItemDragEnded(fromPosition: Int, toPosition: Int) { + runOnUiThread { + suspend = true + val count = Character.charCount(mItemList[toPosition].toInt()) + val (from, to) = if (fromPosition < toPosition) { + val p = mItemList.subList(0, fromPosition).sumOf { ch -> + Character.charCount(ch.toInt()) + } + p to p + mItemList.subList(fromPosition, toPosition).sumOf { ch -> + Character.charCount(ch.toInt()) + } + } else { + val p = mItemList.subList(0, toPosition).sumOf { ch -> + Character.charCount(ch.toInt()) + } + p + mItemList.subList(toPosition + 1, fromPosition + 1).sumOf { ch -> + Character.charCount(ch.toInt()) + } to p + } + edit.editableText.delete(from, from + count) + edit.editableText.insert(to, String(Character.toChars(mItemList[toPosition].toInt()))) + suspend = false + if (!isSameString(edit.editableText)) { + setString(edit.editableText) + } } } @@ -63,70 +112,62 @@ internal class EditAdapter(activity: Activity, db: NameDatabase, single: Boolean return R.string.edit } - override fun getCount(): Int { - return list.size - } - override fun getItemCodePoint(i: Int): Long { - return list[i].toLong() + return (mItemList[i] and 0x1FFFFF).toLong() } override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { if (suspend) return - if (before == 0 && count == 0) return - val str = s.toString() - list.clear() - var i = 0 - while (i < str.length) { - val code = str.codePointAt(i) - list.add(code) - i += Character.charCount(code) + if (count == 0) return + val startCp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + s.subSequence(0, start).codePoints().count().toInt() + } else { + Character.codePointCount(s, 0, start) } - invalidateViews() - } - - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} - override fun afterTextChanged(s: Editable) {} - override fun drop(from: Int, to: Int) { - runOnUiThread { - suspend = true - var fromBegin = 0 - var fromEnd = 0 - for (i in list.indices) { - if (i == from) fromBegin = fromEnd - fromEnd += Character.charCount(list[i]) - if (i == from) break - } - edit.editableText.delete(fromBegin, fromEnd) - val ch = list.removeAt(from) - var toBegin = 0 - for (i in list.indices) { - if (i == to) break - toBegin += Character.charCount(list[i]) + val cps = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + s.subSequence(start, start + count).codePoints().toList() + } else { + val list = ArrayList() + for (i in 0 until count) { + if (Character.isLowSurrogate(s[start + i])) continue + list.add(Character.codePointAt(s, start + i)) } - edit.editableText.insert(toBegin, String(Character.toChars(ch))) - edit.editableText.replace(0, edit.editableText.length, edit.editableText) - suspend = false - list.add(to, ch) - invalidateViews() + list + } + cps.forEachIndexed { index, i -> + mItemList.add(startCp + index, i.toLong() or (sequence++ shl 32)) + } + notifyItemRangeInserted(startCp, cps.size) + if (!isSameString(edit.editableText)) { + setString(edit.editableText) } } - override fun remove(which: Int) { - runOnUiThread { - suspend = true - var whichBegin = 0 - var whichEnd = 0 - for (i in list.indices) { - if (i == which) whichBegin = whichEnd - whichEnd += Character.charCount(list[i]) - if (i == which) break - } - edit.editableText.delete(whichBegin, whichEnd) - edit.editableText.replace(0, edit.editableText.length, edit.editableText) - suspend = false - list.removeAt(which) - invalidateViews() + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + if (suspend) return + if (count == 0) return + if (!isSameString(edit.editableText)) { + setString(edit.editableText) + } + val startCp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + s.subSequence(0, start).codePoints().count().toInt() + } else { + Character.codePointCount(s, 0, start) + } + val countCp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + s.subSequence(start, start + count).codePoints().count().toInt() + } else { + Character.codePointCount(s, start, start + count) } + for (i in 0 until countCp) { + mItemList.removeAt(startCp) + } + notifyItemRangeRemoved(startCp, countCp) + } + + override fun afterTextChanged(s: Editable) {} + + init { + mItemList = ArrayList() } } \ No newline at end of file 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 6aa5818..5a62b9b 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/EmojiAdapter.kt @@ -26,7 +26,7 @@ import android.widget.AdapterView.OnItemSelectedListener import androidx.recyclerview.widget.RecyclerView import java.util.* -internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private val db: NameDatabase, single: Boolean) : UnicodeAdapter(activity, db, single) { +internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private val db: NameDatabase, single: Boolean) : RecyclerUnicodeAdapter(activity, db, single) { private var cur: Cursor? = null private var jump: Spinner? = null private lateinit var map: NavigableMap @@ -183,9 +183,9 @@ internal class EmojiAdapter(activity: Activity, pref: SharedPreferences, private return grp[i] } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { super.onBindViewHolder(holder, position) - if (holder is CharacterViewHolder) holder.characterView.drawSlash(false) + if (holder is UnicodeAdapter.CharacterViewHolder) holder.characterView.drawSlash(false) } override fun save(edit: SharedPreferences.Editor) { diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/FavoriteAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/FavoriteAdapter.kt index 4831072..fa3a76d 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/FavoriteAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/FavoriteAdapter.kt @@ -17,92 +17,70 @@ package jp.ddo.hotmist.unicodepad import android.app.Activity import android.content.SharedPreferences -import com.mobeta.android.dslv.DragSortListView.DropListener -import com.mobeta.android.dslv.DragSortListView.RemoveListener +import androidx.recyclerview.widget.RecyclerView import java.util.* -internal class FavoriteAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : UnicodeAdapter(activity, db, single), DropListener, RemoveListener { - private var list: ArrayList - private var temp: ArrayList - override fun name(): Int { - return R.string.favorite +internal class FavoriteAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : DragListUnicodeAdapter(activity, db, single) { + override fun getUniqueItemId(position: Int): Long { + return getItemCodePoint(position) } - override fun show() { - truncate() - invalidateViews() + override fun onItemDragStarted(position: Int) { } - override fun leave() { - commit() - invalidateViews() + override fun onItemDragging(itemPosition: Int, x: Float, y: Float) { } - override fun getCount(): Int { - return temp.size + override fun onItemDragEnded(fromPosition: Int, toPosition: Int) { } - override fun getItemCodePoint(arg0: Int): Long { - return temp[arg0].toLong() + override fun name(): Int { + return R.string.favorite } - fun add(code: Int) { - list.remove(Integer.valueOf(code)) - list.add(code) + override fun show() { } - fun rem(code: Int) { - list.remove(Integer.valueOf(code)) + override fun leave() { } - private fun commit() { - runOnUiThread { - if (list !== temp) { - temp = list - } + override fun getItemCodePoint(i: Int): Long { + return mItemList[i].toLong() + } + + fun add(code: Int) { + val position = mItemList.indexOf(code) + if (position != -1) { + changeItemPosition(position, 0) + } else { + addItem(itemCount, code) } } - private fun truncate() { - runOnUiThread { - if (list === temp) temp = ArrayList(list) + fun rem(code: Int) { + val position = mItemList.indexOf(code) + if (position != -1) { + removeItem(position) } } fun isFavorite(code: Int): Boolean { - return list.contains(code) + return mItemList.contains(code) } override fun save(edit: SharedPreferences.Editor) { var str = "" - for (i in list) str += String(Character.toChars(i)) + for (i in mItemList) str += String(Character.toChars(i)) edit.putString("fav", str) } - override fun drop(from: Int, to: Int) { - runOnUiThread { - list = temp - list.add(to, list.removeAt(from)) - truncate() - invalidateViews() - } - } - - override fun remove(which: Int) { - runOnUiThread { - list.remove(temp.removeAt(which)) - invalidateViews() - } - } - init { - list = ArrayList() - temp = list + mItemList = ArrayList() val str = pref.getString("fav", null) ?: "" var i = 0 while (i < str.length) { val code = str.codePointAt(i) - list.add(code) + mItemList.add(code) i += Character.charCount(code) } } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/FindAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/FindAdapter.kt index 7453d53..93a6c4e 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/FindAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/FindAdapter.kt @@ -28,7 +28,7 @@ import android.widget.* import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView -internal class FindAdapter(activity: Activity, private val pref: SharedPreferences, private val db: NameDatabase, single: Boolean) : UnicodeAdapter(activity, db, single) { +internal class FindAdapter(activity: Activity, private val pref: SharedPreferences, private val db: NameDatabase, single: Boolean) : RecyclerUnicodeAdapter(activity, db, single) { private var curList: Cursor? = null private var curEmoji: Cursor? = null private var saved: String = pref.getString("find", null) ?: "" diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/ListAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/ListAdapter.kt index fbf0e54..0d2d328 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/ListAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/ListAdapter.kt @@ -21,10 +21,9 @@ import android.app.AlertDialog import android.content.Context import android.content.DialogInterface import android.content.SharedPreferences -import android.content.res.Resources +import android.content.res.Configuration import android.database.DataSetObserver import android.graphics.* -import android.os.Build import android.text.Editable import android.text.InputType import android.text.TextWatcher @@ -43,7 +42,7 @@ import kotlin.math.max import kotlin.math.min -internal class ListAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : UnicodeAdapter(activity, db, single) { +internal class ListAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : RecyclerUnicodeAdapter(activity, db, single) { private data class FromIndex(val codePoint: Int, val block: Int) private data class FromCodePoint(val index: Int, val end: Int, val block: Int) private var count = 0 @@ -540,11 +539,10 @@ internal class ListAdapter(activity: Activity, pref: SharedPreferences, db: Name val jmap = jump.context.resources.getStringArray(R.array.codes).associate { Integer.valueOf(it.substring(0, it.indexOf(' ')), 16) to it.substring(it.indexOf(' ') + 1) } - val jdef = jump.context.resources.let { - jump.context.createConfigurationContext(jump.context.resources.configuration.apply { setLocale(Locale.US) }).resources - }.getStringArray(R.array.codes).associate { - Integer.valueOf(it.substring(0, it.indexOf(' ')), 16) to it.substring(it.indexOf(' ') + 1) - } + val jdef = jump.context.createConfigurationContext(Configuration().apply { setLocale(Locale.US) }).resources + .getStringArray(R.array.codes).associate { + Integer.valueOf(it.substring(0, it.indexOf(' ')), 16) to it.substring(it.indexOf(' ') + 1) + } val jstr = fromCodePoint.keys.map { JumpItem(it, jmap.getValue(it), jdef.getValue(it)) }.toTypedArray() val adp = ArrayAdapter(jump.context, android.R.layout.simple_spinner_item, jstr) adp.setDropDownViewResource(R.layout.spinner_drop_down_item) @@ -685,6 +683,8 @@ internal class ListAdapter(activity: Activity, pref: SharedPreferences, db: Name return var firstItem = firstVisibleItem val titleIndex = searchTitlePosition(firstItem) + if (titleIndex == -1) + return firstItem -= titleIndex if (firstItem != getTitlePosition(titleIndex)) firstItem -= 1 val e2 = fromIndex.floorEntry(firstItem) ?: return @@ -798,9 +798,9 @@ internal class ListAdapter(activity: Activity, pref: SharedPreferences, db: Name return jump?.adapter?.getItem(i).toString() } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { super.onBindViewHolder(holder, position) - val ret = holder.view + val ret = holder.itemView if (position == highlight) { highTarget?.setBackgroundColor(resnormal) highTarget = ret 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 ff5da6a..680d34a 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/NameDatabase.kt @@ -132,17 +132,9 @@ 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_table1510';", null).use { cur -> + db.rawQuery("SELECT * FROM version_code;", null).use { cur -> cur.moveToFirst() - if (cur.getInt(0) != 2) throw SQLiteException() - } - db.rawQuery("SELECT COUNT(*) FROM 'name_table';", null).use { cur -> - cur.moveToFirst() - if (cur.getInt(0) != 34936) throw SQLiteException() - } - db.rawQuery("SELECT COUNT(*) FROM 'emoji_table1510';", null).use { cur -> - cur.moveToFirst() - if (cur.getInt(0) != 3773) throw SQLiteException() + if (cur.getInt(0) != 58) throw SQLiteException() } } catch (e: SQLiteException) { db.close() diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/PageAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/PageAdapter.kt index 241065a..5dd770a 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/PageAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/PageAdapter.kt @@ -21,24 +21,31 @@ import android.content.DialogInterface import android.content.SharedPreferences import android.graphics.Typeface import android.text.style.TextAppearanceSpan -import android.util.TypedValue import android.view.* import android.widget.* import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemLongClickListener +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.PagerAdapter import androidx.viewpager.widget.PagerTabStrip import androidx.viewpager.widget.ViewPager -import com.mobeta.android.dslv.DragSortController -import com.mobeta.android.dslv.DragSortListView -import com.mobeta.android.dslv.DragSortListView.DropListener -import com.mobeta.android.dslv.DragSortListView.RemoveListener +import com.woxthebox.draglistview.DragListView import java.util.* import kotlin.math.max import kotlin.math.min class PageAdapter(private val activity: UnicodeActivity, private val pref: SharedPreferences, private val edit: EditText) : PagerAdapter(), OnItemClickListener, OnItemLongClickListener { + class DynamicDragListView(context: android.content.Context?, attrs: android.util.AttributeSet?) : DragListView(context, attrs) { + init { + super.onFinishInflate() + } + + @SuppressLint("MissingSuperCall") + override fun onFinishInflate() { + } + } + private var numPage: Int private val layouts = arrayOfNulls(MAX_VIEWS) private val views = arrayOfNulls(MAX_VIEWS) @@ -100,47 +107,34 @@ class PageAdapter(private val activity: UnicodeActivity, private val pref: Share adapterEdit.single = bedt bemoji = pref.getString("single_emoji", "false") == "true" adapterEmoji.single = bemoji - return if (adapters[position] is DropListener || adapters[position] is RemoveListener) { - if (adapters[position].single) { - DragSortListView(activity, null).also { view -> - val controller = DragSortController(view, R.id.HANDLE_ID, DragSortController.ON_DRAG, DragSortController.FLING_REMOVE, 0, R.id.HANDLE_ID) - controller.isRemoveEnabled = true - controller.removeMode = DragSortController.FLING_REMOVE - controller.isSortEnabled = true - controller.setBackgroundColor(TypedValue().also { tv -> - activity.theme.resolveAttribute(android.R.attr.windowBackground, tv, true) - }.data) - view.setFloatViewManager(controller) - view.setOnTouchListener(controller) - view.setDropListener(adapters[position] as? DropListener) - view.setRemoveListener(adapters[position] as? RemoveListener) + return adapters[position].let { adapter -> + adapter.setListener(this) + if (adapter is DragListUnicodeAdapter<*> && adapter.single) { + DynamicDragListView(activity, null).also { view -> + view.setLayoutManager(LinearLayoutManager(activity)) + view.setDragListListener(adapter) + view.setAdapter(adapter, false) + view.setCanDragHorizontally(false) + view.setCanDragVertically(true) + view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + views[position] = view + adapter.instantiate(view).also { layout -> + collection.addView(layout, 0) + layouts[position] = layout + } } } else { - GridView(activity).also { view -> - view.numColumns = column - } - }.let { view -> - view.adapter = adapters[position].asBaseAdapter() - view.onItemClickListener = this - view.onItemLongClickListener = this - view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - views[position] = view - adapters[position].instantiate(view).also { layout -> - collection.addView(layout, 0) - layouts[position] = layout - } - } - } else { - RecyclerView(activity).let { view -> - view.adapter = adapters[position] - view.layoutManager = adapters[position].getLayoutManager(activity, column) - adapters[position].setListener(this) - view.adapter = adapters[position] - view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - views[position] = view - adapters[position].instantiate(view).also { layout -> - collection.addView(layout, 0) - layouts[position] = layout + check(adapter is RecyclerView.Adapter<*>) + RecyclerView(activity).let { view -> + view.adapter = adapter + view.layoutManager = adapter.getLayoutManager(activity, column) + view.adapter = adapter + view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + views[position] = view + adapter.instantiate(view).also { layout -> + collection.addView(layout, 0) + layouts[position] = layout + } } } } @@ -158,7 +152,7 @@ class PageAdapter(private val activity: UnicodeActivity, private val pref: Share } override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - onItemClick(((if (parent is DragSortListView) parent.inputAdapter else parent?.adapter) as? BaseUnicodeAdapter?)?.getBaseAdapter(), position, id) + onItemClick(parent?.adapter as? UnicodeAdapter, position, id) } fun onItemClick(adapter: UnicodeAdapter?, position: Int, id: Long) { @@ -175,7 +169,7 @@ class PageAdapter(private val activity: UnicodeActivity, private val pref: Share } override fun onItemLongClick(parent: AdapterView<*>, view: View, position: Int, id: Long): Boolean { - onItemLongClick(((if (parent is DragSortListView) parent.inputAdapter else parent.adapter) as BaseUnicodeAdapter).getBaseAdapter(), position) + onItemLongClick(parent.adapter as UnicodeAdapter, position) return true } @@ -193,7 +187,7 @@ class PageAdapter(private val activity: UnicodeActivity, private val pref: Share layoutParams.gravity = Gravity.TOP val pager = ViewPager(activity) pager.addView(tab, layoutParams) - val adapter = CharacterAdapter(activity, ua, tf, locale, db, adapterFavorite) + val adapter = CharacterAdapter(activity, ua.freeze(), tf, locale, db, adapterFavorite) pager.adapter = adapter pager.setCurrentItem(index, false) pager.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (activity.resources.displayMetrics.scaledDensity * (CharacterAdapter.fontsize * 1.8f + TextAppearanceSpan(activity, android.R.style.TextAppearance_Small).textSize * 2.4f + 32f)).toInt()) diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/RecentAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/RecentAdapter.kt index a9c910f..ca03957 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/RecentAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/RecentAdapter.kt @@ -17,87 +17,59 @@ package jp.ddo.hotmist.unicodepad import android.app.Activity import android.content.SharedPreferences -import com.mobeta.android.dslv.DragSortListView.DropListener -import com.mobeta.android.dslv.DragSortListView.RemoveListener +import androidx.recyclerview.widget.RecyclerView import java.util.* -internal class RecentAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : UnicodeAdapter(activity, db, single), DropListener, RemoveListener { - private var list = ArrayList() - private var temp = list - override fun name(): Int { - return R.string.recent +internal class RecentAdapter(activity: Activity, pref: SharedPreferences, db: NameDatabase, single: Boolean) : DragListUnicodeAdapter(activity, db, single) { + override fun getUniqueItemId(position: Int): Long { + return getItemCodePoint(position) } - override fun show() { - truncate() - invalidateViews() + override fun onItemDragStarted(position: Int) { } - override fun leave() { - commit() - invalidateViews() + override fun onItemDragging(itemPosition: Int, x: Float, y: Float) { } - override fun getCount(): Int { - return temp.size + override fun onItemDragEnded(fromPosition: Int, toPosition: Int) { } - override fun getItemCodePoint(arg0: Int): Long { - return temp[temp.size - arg0 - 1].toLong() + override fun name(): Int { + return R.string.recent } - fun add(code: Int) { - runOnUiThread { - list.remove(Integer.valueOf(code)) - list.add(code) - if (list.size >= maxitems) list.removeAt(0) - invalidateViews() - } + override fun show() { } - fun rem(code: Int) { - runOnUiThread { - list.remove(Integer.valueOf(code)) - if (list !== temp) temp.remove(Integer.valueOf(code)) - invalidateViews() - } + override fun leave() { + } + + override fun getItemCodePoint(i: Int): Long { + return mItemList[i].toLong() } - private fun commit() { - runOnUiThread { - if (list !== temp) temp = list + fun add(code: Int) { + val position = mItemList.indexOf(code) + if (position != -1) { + changeItemPosition(position, 0) + } else { + addItem(0, code) } } - private fun truncate() { - runOnUiThread { - if (list === temp) temp = ArrayList(list) + fun rem(code: Int) { + val position = mItemList.indexOf(code) + if (position != -1) { + removeItem(position) } } override fun save(edit: SharedPreferences.Editor) { var str = "" - for (i in list) str += String(Character.toChars(i)) + for (i in mItemList) str += String(Character.toChars(i)) edit.putString("rec", str) } - override fun drop(from: Int, to: Int) { - runOnUiThread { - list = temp - val i = temp.removeAt(temp.size - from - 1) - temp.add(temp.size - to, i) - truncate() - invalidateViews() - } - } - - override fun remove(which: Int) { - runOnUiThread { - list.remove(temp.removeAt(temp.size - which - 1)) - invalidateViews() - } - } - companion object { var maxitems = 16 } @@ -112,9 +84,10 @@ internal class RecentAdapter(activity: Activity, pref: SharedPreferences, db: Na i += Character.charCount(code) } i = 0 + mItemList = ArrayList() while (i < str.length) { val code = str.codePointAt(i) - if (--num < maxitems) list.add(code) + if (--num < maxitems) mItemList.add(code) i += Character.charCount(code) } } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/SettingActivity.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/SettingActivity.kt index 734a5b1..9c0696c 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/SettingActivity.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/SettingActivity.kt @@ -18,32 +18,31 @@ package jp.ddo.hotmist.unicodepad import android.app.AlertDialog -import android.content.Context import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.LocaleList import android.text.ClipboardManager import android.view.ViewGroup import android.widget.CheckBox import android.widget.CompoundButton import android.widget.LinearLayout import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity import androidx.preference.* import org.json.JSONException import org.json.JSONObject import java.io.IOException import java.util.* -class SettingActivity : AppCompatActivity() { +class SettingActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { - val pref = PreferenceManager.getDefaultSharedPreferences(this) - setTheme(THEME[(pref.getString("theme", null)?.toIntOrNull() ?: 2131492983) - 2131492983]) super.onCreate(savedInstanceState) - fragmentManager.beginTransaction().replace(android.R.id.content, MyPreferenceFragment()).commit() + if (savedInstanceState == null) { + fragmentManager.beginTransaction() + .replace(android.R.id.content, MyPreferenceFragment()) + .commit() + } } override fun onSupportNavigateUp(): Boolean { @@ -51,16 +50,6 @@ class SettingActivity : AppCompatActivity() { return true } - override fun attachBaseContext(newBase: Context?) { - applyOverrideConfiguration(newBase?.resources?.configuration?.also { - it.setLocale(Locale.getDefault()) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - it.setLocales(LocaleList.getDefault()) - } - }) - super.attachBaseContext(newBase) - } - class MyPreferenceFragment : PreferenceFragment(), Preference.OnPreferenceChangeListener { private val adCompat: AdCompat = AdCompatImpl() @@ -168,7 +157,10 @@ class SettingActivity : AppCompatActivity() { } catch (e: NumberFormatException) { return false } - if (key == "theme" || key == "emojicompat") { + if (key == "theme") { + activity.recreate() + } + if (key == "emojicompat") { Toast.makeText(activity, R.string.theme_title, Toast.LENGTH_SHORT).show() activity.setResult(RESULT_FIRST_USER) } @@ -430,10 +422,6 @@ class SettingActivity : AppCompatActivity() { } companion object { - private val THEME = intArrayOf( - androidx.appcompat.R.style.Theme_AppCompat, - androidx.appcompat.R.style.Theme_AppCompat_Light, - androidx.appcompat.R.style.Theme_AppCompat_Light_DarkActionBar) private const val SETTING_EXPORT_CODE = 43 private const val SETTING_IMPORT_CODE = 44 } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/StringAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/StringAdapter.kt index 27649a8..51e7756 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/StringAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/StringAdapter.kt @@ -18,7 +18,7 @@ package jp.ddo.hotmist.unicodepad import android.app.Activity import java.util.* -internal class StringAdapter(str: String, activity: Activity, db: NameDatabase) : UnicodeAdapter(activity, db, false) { +internal class StringAdapter(str: String, activity: Activity, db: NameDatabase) : RecyclerUnicodeAdapter(activity, db, false) { private val list: ArrayList = ArrayList() override fun getCount(): Int { return list.size diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsActivity.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsActivity.kt index 0c126b9..9f2a090 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsActivity.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsActivity.kt @@ -17,26 +17,30 @@ package jp.ddo.hotmist.unicodepad import android.annotation.SuppressLint import android.os.Bundle -import android.util.TypedValue -import androidx.appcompat.app.AppCompatActivity -import androidx.preference.PreferenceManager -import com.mobeta.android.dslv.DragSortController -import com.mobeta.android.dslv.DragSortListView +import androidx.recyclerview.widget.LinearLayoutManager +import com.woxthebox.draglistview.DragListView + +class TabsActivity : BaseActivity() { + class DynamicDragListView(context: android.content.Context?, attrs: android.util.AttributeSet?) : DragListView(context, attrs) { + init { + super.onFinishInflate() + } + + @SuppressLint("MissingSuperCall") + override fun onFinishInflate() { + } + } -class TabsActivity : AppCompatActivity() { @SuppressLint("ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { - setTheme(THEME[(PreferenceManager.getDefaultSharedPreferences(this).getString("theme", null)?.toIntOrNull() ?: 2131492983) - 2131492983]) super.onCreate(savedInstanceState) - val view = DragSortListView(this, null) - val controller = DragSortController(view, R.id.HANDLE_ID, DragSortController.ON_DRAG, DragSortController.FLING_REMOVE) - controller.isSortEnabled = true - controller.setBackgroundColor(TypedValue().also { tv -> - theme.resolveAttribute(android.R.attr.windowBackground, tv, true) - }.data) - view.setFloatViewManager(controller) - view.setOnTouchListener(controller) - view.adapter = TabsAdapter(this, view) + val view = DynamicDragListView(this, null) + val adapter = TabsAdapter(this) + view.setLayoutManager(LinearLayoutManager(this)) + view.setDragListListener(adapter) + view.setAdapter(adapter, true) + view.setCanDragHorizontally(false) + view.setCanDragVertically(true) setContentView(view) } @@ -44,11 +48,4 @@ class TabsActivity : AppCompatActivity() { finish() return true } - - companion object { - private val THEME = intArrayOf( - R.style.Theme, - R.style.Theme_Light, - R.style.Theme_Light_DarkActionBar) - } -} \ No newline at end of file +} diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsAdapter.kt index 63d2343..075f8e5 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/TabsAdapter.kt @@ -15,75 +15,56 @@ */ package jp.ddo.hotmist.unicodepad -import android.annotation.SuppressLint import android.app.Activity import android.content.SharedPreferences -import android.graphics.Typeface -import android.util.TypedValue -import androidx.preference.PreferenceManager import android.view.View +import android.view.View.OnClickListener import android.view.ViewGroup import android.widget.* -import com.mobeta.android.dslv.DragSortListView.DropListener +import androidx.preference.PreferenceManager +import com.woxthebox.draglistview.DragItemAdapter +import com.woxthebox.draglistview.DragListView import java.util.* import kotlin.math.max -class TabsAdapter internal constructor(private val activity: Activity, private val list: AbsListView?) : BaseAdapter(), View.OnClickListener, DropListener { + +class TabsAdapter internal constructor(private val activity: Activity) : DragItemAdapter(), OnClickListener, DragListView.DragListListener { private val pref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity) private val idx: ArrayList = ArrayList(NUM_TABS + 2) private val single: ArrayList = ArrayList(NUM_TABS) private var shownNum = pref.getInt("cnt_shown", NUM_TABS) - override fun getCount(): Int { - return NUM_TABS + 2 - } - override fun getViewTypeCount(): Int { - return 2 + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return if (viewType == 0) { + HeaderViewHolder(activity.layoutInflater.inflate(R.layout.spinwidget_title, parent, false)) + } else { + ItemViewHolder(activity.layoutInflater.inflate(R.layout.spinwidget, parent, false).also { + it.findViewById(R.id.tabs_multiple).setOnClickListener(this) + it.findViewById(R.id.tabs_single).setOnClickListener(this) + }) + } } - override fun getItemViewType(position: Int): Int { - if (position == 0) return 0 - return if (position == shownNum + 1) 0 else 1 + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + if (holder is HeaderViewHolder) { + holder.title.setText(if (position == 0) R.string.shown_desc else R.string.hidden_desc) + } else if (holder is ItemViewHolder) { + val i = idx[position] + holder.title.setText(RESOURCES[i]) + holder.multiple.isChecked = !single[i] + holder.multiple.tag = i + holder.single.isChecked = single[i] + holder.single.tag = i + } } - override fun getItem(i: Int): Any { - return Any() + override fun getItemViewType(position: Int): Int { + return if (idx[position] < 0) 0 else 1 } - override fun getItemId(i: Int): Long { - return idx[i].toLong() - } - - @SuppressLint("InflateParams") - override fun getView(i: Int, view: View?, viewGroup: ViewGroup): View { - return if (getItemViewType(i) == 0) { - (view as TextView? ?: TextView(activity).also { - it.setTextColor(TypedValue().also { tv -> - activity.theme.resolveAttribute(android.R.attr.textColorLink, tv, true) - }.data) - it.typeface = Typeface.DEFAULT_BOLD - (activity.resources.displayMetrics.density * 16).toInt().let { padding -> - it.setPadding(padding, padding / 2, padding, padding / 2) - } - }).also { - it.setText(if (i == 0) R.string.shown_desc else R.string.hidden_desc) - } - } else { - (view ?: activity.layoutInflater.inflate(R.layout.spinwidget, null).also { - it.findViewById(R.id.tabs_multiple).setOnClickListener(this) - it.findViewById(R.id.tabs_single).setOnClickListener(this) - }).also { - it.findViewById(R.id.tabs_title).setText(RESOURCES[idx[i]]) - it.findViewById(R.id.tabs_multiple).let { rb -> - rb.isChecked = !single[idx[i]] - rb.tag = idx[i] - } - it.findViewById(R.id.tabs_single).let { rb -> - rb.isChecked = single[idx[i]] - rb.tag = idx[i] - } - } - } + override fun getUniqueItemId(position: Int): Long { + return idx[position].toLong() } override fun onClick(view: View) { @@ -94,18 +75,22 @@ class TabsAdapter internal constructor(private val activity: Activity, private v edit.apply() } - override fun drop(from: Int, to: Int) { - if (idx[from] == 1 && to > shownNum) { - Toast.makeText(activity, R.string.list_title, Toast.LENGTH_SHORT).show() - return - } - if (from < shownNum + 1) --shownNum - if (shownNum == 0) { - ++shownNum + override fun onItemDragStarted(position: Int) { + } + + override fun onItemDragging(itemPosition: Int, x: Float, y: Float) { + } + + override fun onItemDragEnded(fromPosition: Int, toPosition: Int) { + if (idx[toPosition] == 1 && toPosition > shownNum || idx[0] != -2) { + idx.add(max(fromPosition, 1), idx.removeAt(toPosition)) + notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1) + if (idx[0] == -2) { + Toast.makeText(activity, R.string.list_title, Toast.LENGTH_SHORT).show() + } return } - if (to <= shownNum + 1) ++shownNum - idx.add(max(to, 1), idx.removeAt(from)) + shownNum = idx.indexOf(-3) - 1 val edit = pref.edit() edit.putInt("cnt_shown", shownNum) for (i in 1 until NUM_TABS + 2) { @@ -115,14 +100,25 @@ class TabsAdapter internal constructor(private val activity: Activity, private v edit.putInt("ord_" + KEYS[idx[i]], ord) } edit.apply() - list?.invalidateViews() + } + + abstract class ViewHolder(itemView: View) : DragItemAdapter.ViewHolder(itemView, R.id.HANDLE_ID, false) + + class HeaderViewHolder(itemView: View) : ViewHolder(itemView) { + val title: TextView = itemView.findViewById(R.id.tabs_title) + } + + class ItemViewHolder(itemView: View) : ViewHolder(itemView) { + val title: TextView = itemView.findViewById(R.id.tabs_title) + val multiple: RadioButton = itemView.findViewById(R.id.tabs_multiple) + val single: RadioButton = itemView.findViewById(R.id.tabs_single) } init { for (i in 0 until NUM_TABS + 2) idx.add(-2) for (i in 0 until NUM_TABS) single.add(false) - idx[0] = -1 - idx[shownNum + 1] = -1 + idx[0] = -2 + idx[shownNum + 1] = -3 for (i in 0 until NUM_TABS) { var ord = pref.getInt("ord_" + KEYS[i], i) + 1 if (ord > shownNum) ++ord @@ -132,6 +128,7 @@ class TabsAdapter internal constructor(private val activity: Activity, private v } single[i] = pref.getString("single_" + KEYS[i], null)?.toBoolean() ?: DEFAULTS[i] } + mItemList = idx } companion object { 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 1d94aaa..c85b40e 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeActivity.kt @@ -31,7 +31,6 @@ import android.view.* import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.* -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.getResourceIdOrThrow import androidx.core.provider.FontRequest import androidx.core.view.doOnLayout @@ -53,7 +52,7 @@ import kotlin.math.min @Suppress("DEPRECATION") -class UnicodeActivity : AppCompatActivity() { +class UnicodeActivity : BaseActivity() { private lateinit var editText: EditText private lateinit var btnClear: ImageButton private lateinit var btnRow: LinearLayout @@ -98,7 +97,6 @@ class UnicodeActivity : AppCompatActivity() { } })) } - setTheme(THEME[(pref.getString("theme", null)?.toIntOrNull() ?: 2131492983) - 2131492983]) super.onCreate(savedInstanceState) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) setContentView(if (useEmoji == "null") R.layout.main else R.layout.main_emojicompat) @@ -616,10 +614,6 @@ class UnicodeActivity : AppCompatActivity() { private const val MENU_ID_SHARE = 36 private const val MENU_ID_SEND = 37 private const val MAX_HISTORY = 256 - private val THEME = intArrayOf( - R.style.Theme, - R.style.Theme_Light, - R.style.Theme_Light_DarkActionBar) private var fontsize = 24.0f internal var univer = 1000 } diff --git a/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeAdapter.kt b/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeAdapter.kt index 3bc3d3b..0a66aa4 100644 --- a/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeAdapter.kt +++ b/app/src/main/java/jp/ddo/hotmist/unicodepad/UnicodeAdapter.kt @@ -18,83 +18,215 @@ package jp.ddo.hotmist.unicodepad import android.app.Activity import android.content.Context import android.content.SharedPreferences -import android.database.DataSetObserver import android.graphics.Typeface import android.util.TypedValue import android.view.* import android.widget.* import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.mobeta.android.dslv.DragSortListView.DropListener -import com.mobeta.android.dslv.DragSortListView.RemoveListener +import com.woxthebox.draglistview.DragItemAdapter +import com.woxthebox.draglistview.DragListView import java.util.* - -interface BaseUnicodeAdapter { - fun getBaseAdapter(): UnicodeAdapter -} - -abstract class UnicodeAdapter(protected val activity: Activity, private val db: NameDatabase, var single: Boolean) : RecyclerView.Adapter(), BaseUnicodeAdapter { - private var typeface: Typeface? = null - private var locale = Locale.ROOT - protected var view: View? = null - internal var lastPadding: Int = 0 - private var onItemClickListener: View.OnClickListener? = null - private var onItemLongClickListener: View.OnLongClickListener? = null - private val reslist = TypedValue().also { - activity.theme.resolveAttribute(android.R.attr.selectableItemBackground, it, true) - }.resourceId - - init { - setHasStableIds(false) - } - - open fun name(): Int { +import kotlin.collections.ArrayList + +interface UnicodeAdapter { + val activity: Activity + var single: Boolean + var typeface: Typeface? + var locale: Locale + var view: View? + var lastPadding: Int + var onItemClickListener: View.OnClickListener? + var onItemLongClickListener: View.OnLongClickListener? + val reslist: Int + + fun name(): Int { return 0 } - open fun instantiate(view: View): View { + fun instantiate(view: View): View { this.view = view return view } - open fun destroy() { + fun destroy() { view = null } - open fun invalidateViews() { - view.let { - when (it) { - is AbsListView -> { - baseAdapter.notifyDataSetChanged() - it.invalidateViews() - } - is RecyclerView -> notifyDataSetChanged() - } - } + fun freeze(): UnicodeAdapter { + return this } - open fun save(edit: SharedPreferences.Editor) {} - open fun show() {} - open fun leave() {} + fun invalidateViews() - open fun getItemCodePoint(i: Int): Long { + fun save(edit: SharedPreferences.Editor) {} + fun show() {} + fun leave() {} + + fun getItemCodePoint(i: Int): Long { return -1 } - open fun getItemString(i: Int): String { + fun getItemString(i: Int): String { return String.format("%04X", getItemCodePoint(i).toInt()) } - open fun getItem(i: Int): String { + fun getItem(i: Int): String { return String(Character.toChars(getItemCodePoint(i).toInt())) } + fun getCount(): Int + + fun getItemIndex(position: Int): Pair { + return position to -1 + } + + fun setListener(listener: PageAdapter) { + onItemClickListener = View.OnClickListener { + it.tag.let { tag -> + check(tag is ViewHolder) + val position = getItemIndex(tag.absoluteAdapterPosition).first + check(position >= 0) + listener.onItemClick(this, position, getItemCodePoint(position)) + } + } + onItemLongClickListener = View.OnLongClickListener { + it.tag.let { tag -> + check(tag is ViewHolder) + val position = getItemIndex(tag.absoluteAdapterPosition).first + check(position >= 0) + listener.onItemLongClick(this, position) + } + true + } + } + + fun setTypeface(typeface: Typeface?, locale: Locale) { + this.typeface = typeface + this.locale = locale + invalidateViews() + } + + companion object { + var padding = 3 + var fontsize = 18f + var shrink = true + } + + abstract class ViewHolder(val view: View) : DragItemAdapter.ViewHolder(view, R.id.HANDLE_ID, false) + + abstract class CharacterViewHolder(view: View, val characterView: CharacterView) : ViewHolder(view) + + class CellViewHolder(view: LinearLayout, characterView: CharacterView) : CharacterViewHolder(view, characterView) + + class RowViewHolder(view: LinearLayout, characterView: CharacterView, val codePointView: TextView, val nameView: TextView) : CharacterViewHolder(view, characterView) + + fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return if (single) { + val view = LinearLayout(activity) + view.orientation = LinearLayout.HORIZONTAL + view.addView(ImageView(activity).also { imageView -> + imageView.setImageResource(TypedValue().also { tv -> + activity.theme.resolveAttribute(R.attr.drag_handle, tv, true) + }.resourceId) + imageView.id = R.id.HANDLE_ID + if (this !is DragItemAdapter<*, *>) imageView.visibility = View.GONE + }, LinearLayout.LayoutParams((activity.resources.displayMetrics.scaledDensity * 24).toInt(), ViewGroup.LayoutParams.MATCH_PARENT)) + val characterView = CharacterView(activity, null, android.R.attr.textAppearanceLarge) + view.addView(characterView, LinearLayout.LayoutParams((activity.resources.displayMetrics.scaledDensity * fontsize * 2 + padding * 2).toInt(), ViewGroup.LayoutParams.MATCH_PARENT)) + val codePointView = TextView(activity, null, android.R.attr.textAppearanceSmall) + val nameView = TextView(activity, null, android.R.attr.textAppearanceSmall) + view.addView(LinearLayout(activity).also { linearLayout -> + linearLayout.orientation = LinearLayout.VERTICAL + linearLayout.addView(codePointView.also { textView -> + textView.setPadding(0, 0, 0, 0) + }, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) + linearLayout.addView(nameView.also { textView -> + textView.setPadding(0, 0, 0, 0) + }, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) + }, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)) + view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + RowViewHolder(view, characterView, codePointView, nameView) + } else { + val view = LinearLayout(activity) + view.orientation = LinearLayout.HORIZONTAL + view.addView(View(activity).also { imageView -> + imageView.id = R.id.HANDLE_ID + imageView.visibility = View.GONE + }, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT)) + val characterView = CharacterView(activity, null, android.R.attr.textAppearanceLarge) + view.addView(characterView, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) + CellViewHolder(view, characterView) + }.also { + if (viewType != -1) { + it.view.setOnClickListener(onItemClickListener) + it.view.setOnLongClickListener(onItemLongClickListener) + it.view.setBackgroundResource(reslist) + } + } + } + + fun onBindViewHolder(db: NameDatabase, holder: CharacterViewHolder, position: Int, last: Boolean) { + holder.characterView.let { characterView -> + characterView.setPadding(padding, padding, padding, padding + if (last) lastPadding else 0) + characterView.setTextSize(fontsize) + characterView.shrinkWidth(shrink) + characterView.setTypeface(typeface, locale) + characterView.drawSlash(true) + val ver = if (getItemCodePoint(position) >= 0) db.getInt(getItemCodePoint(position).toInt(), "version") else db.getInt(getItemString(position), "version") + characterView.setValid(ver != 0 && ver <= UnicodeActivity.univer) + characterView.text = getItem(position) + } + if (holder is RowViewHolder) { + if (getItemCodePoint(position) >= 0) { + holder.codePointView.text = String.format("U+%04X", getItemCodePoint(position).toInt()) + holder.nameView.text = db[getItemCodePoint(position).toInt(), "name"] + } else { + holder.codePointView.text = (" " + getItemString(position)).replace(" ", " U+").substring(1) + holder.nameView.text = db[getItemString(position), "name"] + } + } + holder.view.tag = holder + } + + fun runOnUiThread(action: Runnable?) { + activity.runOnUiThread(action) + } + + var layoutManager: GridLayoutManager? + + fun getLayoutManager(context: Context, spanCount: Int): GridLayoutManager { + return GridLayoutManager(context, if (single) 1 else spanCount).also { + layoutManager = it + } + } +} + +abstract class RecyclerUnicodeAdapter(override val activity: Activity, private val db: NameDatabase, override var single: Boolean) : RecyclerView.Adapter(), UnicodeAdapter { + override var typeface: Typeface? = null + override var locale: Locale = Locale.ROOT + override var view: View? = null + override var lastPadding: Int = 0 + override var onItemClickListener: View.OnClickListener? = null + override var onItemLongClickListener: View.OnLongClickListener? = null + override val reslist = TypedValue().also { + activity.theme.resolveAttribute(android.R.attr.selectableItemBackground, it, true) + }.resourceId + + init { + setHasStableIds(false) + } + + override fun invalidateViews() { + notifyDataSetChanged() + } + final override fun getItemId(i: Int): Long { val (itemIndex, titleIndex) = getItemIndex(i) return if (itemIndex == -1) -1L - titleIndex else getItemCodePoint(itemIndex) } - protected fun getItemIndex(position: Int): Pair { + override fun getItemIndex(position: Int): Pair { val titleIndex = searchTitlePosition(position) return if (titleIndex >= 0 && position == getTitlePosition(titleIndex) + titleIndex) -1 to titleIndex @@ -102,8 +234,6 @@ abstract class UnicodeAdapter(protected val activity: Activity, private val db: position - titleIndex - 1 to -1 } - abstract fun getCount(): Int - open fun getTitleCount(): Int { return 0 } @@ -155,132 +285,43 @@ abstract class UnicodeAdapter(protected val activity: Activity, private val db: return if (getTitleCount() > 0 && i == getTitlePosition(titleIndex) + titleIndex) 1 else 0 } - fun setListener(listener: PageAdapter) { - onItemClickListener = View.OnClickListener { - val position = it.tag as Int - listener.onItemClick(this, position, getItemCodePoint(position)) - } - onItemLongClickListener = View.OnLongClickListener { - val position = it.tag as Int - listener.onItemLongClick(this, position) - true - } - } - - abstract class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) - - abstract class CharacterViewHolder(view: View, val characterView: CharacterView) : ViewHolder(view) - - class CellViewHolder(view: CharacterView) : CharacterViewHolder(view, view) - - class RowViewHolder(view: LinearLayout, characterView: CharacterView, val codePointView: TextView, val nameView: TextView) : CharacterViewHolder(view, characterView) - - class GroupViewHolder(view: TextView) : ViewHolder(view) { + class GroupViewHolder(view: TextView) : RecyclerView.ViewHolder(view) { val textView: TextView = view } - class HeaderViewHolder(view: TextView) : ViewHolder(view) { + class HeaderViewHolder(view: TextView) : RecyclerView.ViewHolder(view) { val textView: TextView = view } - final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { 1 -> GroupViewHolder(TextView(activity, null, android.R.attr.textAppearanceSmall)) 2 -> HeaderViewHolder(TextView(activity, null, android.R.attr.textAppearanceSmall)) - else -> if (single) { - val view = LinearLayout(activity) - view.orientation = LinearLayout.HORIZONTAL - view.addView(ImageView(activity).also { imageView -> - imageView.setImageResource(TypedValue().also { tv -> - activity.theme.resolveAttribute(R.attr.drag_handle, tv, true) - }.resourceId) - imageView.id = R.id.HANDLE_ID - if (!(this is DropListener || this is RemoveListener)) imageView.visibility = View.GONE - }, LinearLayout.LayoutParams((activity.resources.displayMetrics.scaledDensity * 24).toInt(), ViewGroup.LayoutParams.MATCH_PARENT)) - val characterView = CharacterView(activity, null, android.R.attr.textAppearanceLarge) - view.addView(characterView, LinearLayout.LayoutParams((activity.resources.displayMetrics.scaledDensity * fontsize * 2 + padding * 2).toInt(), ViewGroup.LayoutParams.MATCH_PARENT)) - val codePointView = TextView(activity, null, android.R.attr.textAppearanceSmall) - val nameView = TextView(activity, null, android.R.attr.textAppearanceSmall) - view.addView(LinearLayout(activity).also { linearLayout -> - linearLayout.orientation = LinearLayout.VERTICAL - linearLayout.addView(codePointView.also { textView -> - textView.setPadding(0, 0, 0, 0) - }, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) - linearLayout.addView(nameView.also { textView -> - textView.setPadding(0, 0, 0, 0) - }, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)) - }, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)) - RowViewHolder(view, characterView, codePointView, nameView) - } else { - CellViewHolder(CharacterView(activity, null, android.R.attr.textAppearanceLarge)) - }.also { - if (viewType != -1) { - it.view.setOnClickListener(onItemClickListener) - it.view.setOnLongClickListener(onItemLongClickListener) - it.view.setBackgroundResource(reslist) - } - } + else -> super.onCreateViewHolder(parent, viewType) } } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val (itemIndex, titleIndex) = if (getItemViewType(position) != -1) getItemIndex(position) else position to -1 when (holder) { - is CharacterViewHolder -> { - holder.characterView.let { characterView -> - characterView.setPadding(padding, padding, padding, padding + if (holder is CellViewHolder && position == itemCount - 1) lastPadding else 0) - characterView.setTextSize(fontsize) - characterView.shrinkWidth(shrink) - characterView.setTypeface(typeface, locale) - characterView.drawSlash(true) - val ver = if (getItemCodePoint(itemIndex) >= 0) db.getInt(getItemCodePoint(itemIndex).toInt(), "version") else db.getInt(getItemString(itemIndex), "version") - characterView.setValid(ver != 0 && ver <= UnicodeActivity.univer) - characterView.text = getItem(itemIndex) - } - if (holder is RowViewHolder) { - if (getItemCodePoint(itemIndex) >= 0) { - holder.codePointView.text = String.format("U+%04X", getItemCodePoint(itemIndex).toInt()) - holder.nameView.text = db[getItemCodePoint(itemIndex).toInt(), "name"] - } else { - holder.codePointView.text = (" " + getItemString(itemIndex)).replace(" ", " U+").substring(1) - holder.nameView.text = db[getItemString(itemIndex), "name"] - } - } - holder.view.tag = itemIndex - } + is UnicodeAdapter.CharacterViewHolder -> super.onBindViewHolder(db, holder, itemIndex, position == itemCount - 1) is GroupViewHolder -> { holder.textView.text = getTitleString(titleIndex) } is HeaderViewHolder -> { } } - if (holder !is CellViewHolder) { - holder.view.setPadding(0, 0, 0, if (position == itemCount - 1) lastPadding else 0) + if (holder !is UnicodeAdapter.CharacterViewHolder) { + holder.itemView.setPadding(0, 0, 0, if (position == itemCount - 1) lastPadding else 0) } } - final override fun getBaseAdapter(): UnicodeAdapter { - return this - } - - protected fun runOnUiThread(action: Runnable?) { + override fun runOnUiThread(action: Runnable?) { activity.runOnUiThread(action) } - fun setTypeface(typeface: Typeface?, locale: Locale) { - this.typeface = typeface - this.locale = locale - invalidateViews() - } - - companion object { - var padding = 3 - var fontsize = 18f - var shrink = true - } - - private var layoutManager: GridLayoutManager? = null + override var layoutManager: GridLayoutManager? = null open fun instantiateSpanSizeLookup(context: Context, spanCount: Int): GridLayoutManager.SpanSizeLookup { return object : GridLayoutManager.SpanSizeLookup() { @@ -292,87 +333,74 @@ abstract class UnicodeAdapter(protected val activity: Activity, private val db: } } - fun getLayoutManager(context: Context, spanCount: Int): GridLayoutManager { + override fun getLayoutManager(context: Context, spanCount: Int): GridLayoutManager { return GridLayoutManager(context, if (single) 1 else spanCount).also { it.spanSizeLookup = instantiateSpanSizeLookup(context, spanCount) layoutManager = it } } +} - private lateinit var baseAdapter: BaseAdapter - fun asBaseAdapter(): BaseAdapter { - if (this::baseAdapter.isInitialized) return baseAdapter - baseAdapter = BaseAdapterWrapper(this) - return baseAdapter - } - - class BaseAdapterWrapper(val adapter: UnicodeAdapter) : BaseAdapter(), BaseUnicodeAdapter { - override fun getCount(): Int { - return adapter.getCount() - } - - override fun getItemId(position: Int): Long { - return adapter.getItemCodePoint(position) - } - - override fun getItem(position: Int): String { - return adapter.getItem(position) - } - - fun getItemString(position: Int): String { - return adapter.getItemString(position) - } +abstract class DragListUnicodeAdapter(override val activity: Activity, private val db: NameDatabase, override var single: Boolean) : DragItemAdapter(), DragListView.DragListListener, UnicodeAdapter { + override var typeface: Typeface? = null + override var locale: Locale = Locale.ROOT + override var view: View? = null + override var lastPadding: Int = 0 + override var onItemClickListener: View.OnClickListener? = null + override var onItemLongClickListener: View.OnLongClickListener? = null + override val reslist = TypedValue().also { + activity.theme.resolveAttribute(android.R.attr.selectableItemBackground, it, true) + }.resourceId - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { - (convertView?.let { - if (adapter.single) { - val view = it as LinearLayout - val characterView = view.getChildAt(1) as CharacterView - val codePointView = (view.getChildAt(2) as LinearLayout).getChildAt(0) as TextView - val nameView = (view.getChildAt(2) as LinearLayout).getChildAt(1) as TextView - RowViewHolder(view, characterView, codePointView, nameView) - } else { - val view = it as CharacterView - CellViewHolder(view) - } - } ?: adapter.onCreateViewHolder(parent!!, -1)).let { - adapter.onBindViewHolder(it, adapter.searchItemPosition(position)) - return it.view + class ClonedDragListUnicodeAdapter(base: DragListUnicodeAdapter) : DragListUnicodeAdapter(base.activity, base.db, base.single) { + init { + mItemList = ArrayList() + for (i in base.mItemList.indices) { + mItemList.add(base.getItemCodePoint(i).toInt()) } } - override fun getBaseAdapter(): UnicodeAdapter { - return adapter + override fun getUniqueItemId(position: Int): Long { + return position.toLong() } - override fun getItemViewType(position: Int): Int { - return 0 + override fun onItemDragStarted(position: Int) { } - override fun getViewTypeCount(): Int { - return 1 + override fun onItemDragging(itemPosition: Int, x: Float, y: Float) { } - override fun hasStableIds(): Boolean { - return adapter.hasStableIds() + override fun onItemDragEnded(fromPosition: Int, toPosition: Int) { } - override fun isEmpty(): Boolean { - return adapter.getCount() == 0 + override fun getItemCodePoint(i: Int): Long { + return mItemList[i].toLong() } + } + override fun freeze(): UnicodeAdapter { + return ClonedDragListUnicodeAdapter(this) + } - override fun registerDataSetObserver(observer: DataSetObserver?) { - } + override fun invalidateViews() { + notifyDataSetChanged() + } - override fun unregisterDataSetObserver(observer: DataSetObserver?) { - } + override fun getCount(): Int { + return itemCount + } - override fun areAllItemsEnabled(): Boolean { - return true - } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UnicodeAdapter.ViewHolder { + return super.onCreateViewHolder(parent, viewType) as UnicodeAdapter.ViewHolder + } - override fun isEnabled(position: Int): Boolean { - return true - } + override fun onBindViewHolder(holder: UnicodeAdapter.ViewHolder, position: Int) { + super.onBindViewHolder(db, holder as UnicodeAdapter.CharacterViewHolder, position, position == itemCount - 1) + super.onBindViewHolder(holder, position) + } + + override fun runOnUiThread(action: Runnable?) { + activity.runOnUiThread(action) } -} \ No newline at end of file + + override var layoutManager: GridLayoutManager? = null +} diff --git a/app/src/main/res/layout/spinwidget.xml b/app/src/main/res/layout/spinwidget.xml index 26140bc..12dc516 100644 --- a/app/src/main/res/layout/spinwidget.xml +++ b/app/src/main/res/layout/spinwidget.xml @@ -17,7 +17,10 @@ + android:background="?android:attr/windowBackground" + android:orientation="horizontal" + android:paddingStart="8dip" + android:paddingEnd="8dip"> + + + + + + + + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 87f87c6..8d9596b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -420,6 +420,7 @@ 黒基調 白基調 白基調 + 黒タイトル + システム既定値 再起動後に適用されます 一覧タブは非表示にできません diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 771d2a7..98bd76a 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -372,15 +372,29 @@ F0000 사용자 영역 보충 A 100000 사용자 영역 보충 B + 다음 + 건너뛰기 + 완료 + 문자판 + Unicode의 모든 문자를 탐색할 수 있습니다. 짧게 눌러 문자를 입력하고 길게 눌러 문자 정보를 확인합니다. + 복사 / 공유 / 완료 + 편집이 끝났다면, Unicode 문자열을 복사하거나 공유할 수 있습니다. + + 이모지, 검색, 즐겨찾기 등 여러 문자판을 이용할 수 있습니다. + 글꼴 선택기 + 글리프(렌더링한 문자)가 누락된 경우 적절한 글꼴을 내려받고 선택합니다. + 설정 + 앱의 동작과 모양을 사용자 지정할 수 있습니다. 또 설정에서 이 안내를 다시 확인할 수 있습니다. + 일반 표시 기능 편집 상자의 글자 크기 열 개수 열 개수 (가로모드) - Unicode 버전 - 최근 (15.1.0) + + 최신 (15.1.0) EmojiCompat으로 이모지 표시 꺼짐 @@ -406,6 +420,7 @@ 스크롤 위치 현재 데이터와 합치기 광고 숨기기 + 안내 건너뛰기 탭 설정… 보이는 탭 숨긴 탭 @@ -426,6 +441,7 @@ 어둡게 밝게 밝게 + 제목 표시줄 어둡게 + 시스템 기본값 재시작 후 적용됨 목록 탭은 숨길 수 없음 @@ -436,7 +452,8 @@ 항상 IME 비활성화 - 초기화 버튼 보이기 + 초기화 버튼 표시 + 버튼 줄 표시 ZIP 파일 이름이 잘못됨 라이선스 This application includes a modified version of Unicode Data File. diff --git a/app/src/main/res/values-night/style.xml b/app/src/main/res/values-night/style.xml new file mode 100644 index 0000000..29cee98 --- /dev/null +++ b/app/src/main/res/values-night/style.xml @@ -0,0 +1,5 @@ + + + + + +