From bd4b61d7ac61f56be4da841c9ff2ba73e5430fef Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 30 Jun 2019 22:04:35 +0900 Subject: [PATCH 1/5] Utilizing Glide Fixed Reader FAB icon Changed to use gallery id instead of galleryblock to open Reader --- .idea/misc.xml | 2 +- app/build.gradle | 6 ++ .../pupil/adapters/GalleryBlockAdapter.kt | 36 +++---- .../quaver/pupil/adapters/ReaderAdapter.kt | 56 +++------- .../quaver/pupil/types/SelectorSuggestion.kt | 13 +++ .../xyz/quaver/pupil/types/TagSuggestion.kt | 2 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 58 +++++----- .../xyz/quaver/pupil/ui/ReaderActivity.kt | 70 ++++++------ .../quaver/pupil/util/GalleryDownloader.kt | 102 ++++++++++-------- app/src/main/res/layout/item_reader.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../src/main/java/xyz/quaver/hitomi/reader.kt | 72 +++++++++++++ .../main/java/xyz/quaver/hitomi/readers.kt | 57 ---------- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 51 ++++----- 16 files changed, 274 insertions(+), 255 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt create mode 100644 libpupil/src/main/java/xyz/quaver/hitomi/reader.kt delete mode 100644 libpupil/src/main/java/xyz/quaver/hitomi/readers.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 84da703c..7631aec3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 989ed597..9b1f14c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlinx-serialization' apply plugin: 'com.google.gms.google-services' @@ -52,8 +53,13 @@ dependencies { implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.clans:fab:1.6.4' + implementation 'com.github.bumptech.glide:glide:4.9.0' + implementation ("com.github.bumptech.glide:recyclerview-integration:4.9.0") { + transitive = false + } implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' implementation "ru.noties.markwon:core:${markwonVersion}" + kapt 'com.github.bumptech.glide:compiler:4.9.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:rules:1.2.0' diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index 68b23485..bf314f69 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -3,7 +3,6 @@ package xyz.quaver.pupil.adapters import android.app.AlertDialog import android.graphics.BitmapFactory import android.graphics.drawable.Drawable -import android.util.Log import android.util.SparseBooleanArray import android.view.LayoutInflater import android.view.View @@ -15,6 +14,8 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.CoroutineScope @@ -23,9 +24,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.ReaderItem +import xyz.quaver.hitomi.Reader import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tag @@ -47,8 +47,8 @@ class GalleryBlockAdapter(private val galleries: List>) { + inner class GalleryViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) { + fun bind(holder: GalleryViewHolder, item: Pair>) { with(view) { val resources = context.resources val languages = resources.getStringArray(R.array.languages).map { @@ -62,17 +62,15 @@ class GalleryBlockAdapter(private val galleries: List) : RecyclerView.Adapter() { @@ -25,47 +25,19 @@ class ReaderAdapter(private val images: List) : RecyclerView.Adapter reqHeight || width > reqWidth) { - - val halfHeight: Int = height / 2 - val halfWidth: Int = width / 2 - - // Calculate the largest inSampleSize value that is a power of 2 and keeps both - // height and width larger than the requested height and width. - while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { - inSampleSize *= 2 - } - } - - return inSampleSize + val progressDrawable = CircularProgressDrawable(holder.view.context).apply { + strokeWidth = 10f + centerRadius = 100f + start() } - with(holder.view as ImageView) { - val options = BitmapFactory.Options() - - options.inJustDecodeBounds = true - BitmapFactory.decodeFile(images[position], options) - - val (reqWidth, reqHeight) = context.resources.displayMetrics.let { - Pair(it.widthPixels, it.heightPixels) - } - - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) - - options.inPreferredConfig = Bitmap.Config.RGB_565 - - options.inJustDecodeBounds = false - - val image = BitmapFactory.decodeFile(images[position], options) - - setImageBitmap(image) - } + Glide.with(holder.view) + .load(images[position]) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .placeholder(progressDrawable) + .error(R.drawable.image_broken_variant) + .into(holder.view as ImageView) } override fun getItemCount() = images.size diff --git a/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt b/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt new file mode 100644 index 00000000..a350228c --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt @@ -0,0 +1,13 @@ +package xyz.quaver.pupil.types + +import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion +import kotlinx.android.parcel.Parcelize +import xyz.quaver.hitomi.Suggestion + +@Parcelize +class SelectorSuggestion : SearchSuggestion { + + override fun getBody(): String { + return "" + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt b/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt index 2b39db9f..f46a83a0 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt @@ -5,7 +5,7 @@ import kotlinx.android.parcel.Parcelize import xyz.quaver.hitomi.Suggestion @Parcelize -data class TagSuggestion constructor(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { +data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n) override fun getBody(): String { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index a19254a9..084020b1 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -55,6 +55,8 @@ import java.net.URL import java.util.* import javax.net.ssl.HttpsURLConnection import kotlin.collections.ArrayList +import kotlin.math.abs +import kotlin.math.ceil import kotlin.math.min import kotlin.math.roundToInt @@ -72,8 +74,10 @@ class MainActivity : AppCompatActivity() { private var query = "" set(value) { field = value - findViewById(R.id.search_bar_text) - .setText(query, TextView.BufferType.EDITABLE) + with(findViewById(R.id.search_bar_text)) { + if (text.toString() != value) + setText(query, TextView.BufferType.EDITABLE) + } } private var mode = Mode.SEARCH @@ -155,7 +159,7 @@ class MainActivity : AppCompatActivity() { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { val preference = PreferenceManager.getDefaultSharedPreferences(this) val perPage = preference.getString("per_page", "25")!!.toInt() - val maxPage = Math.ceil(totalItems / perPage.toDouble()).roundToInt() + val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt() return when(keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { @@ -380,9 +384,9 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this@MainActivity, ReaderActivity::class.java) val gallery = galleries[position].first - intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) + intent.putExtra("galleryID", gallery.id) - //TODO: Maybe sprinke some transitions will be nice :D + //TODO: Maybe sprinkling some transitions will be nice :D startActivity(intent) histories.add(gallery.id) @@ -391,7 +395,7 @@ class MainActivity : AppCompatActivity() { if (v !is CardView) return@setOnItemLongClickListener true - val galleryBlock = galleries[position].first + val gallery = galleries[position].first val view = LayoutInflater.from(this@MainActivity) .inflate(R.layout.dialog_galleryblock, recyclerView, false) @@ -400,15 +404,15 @@ class MainActivity : AppCompatActivity() { }.create() with(view.main_dialog_download) { - text = when(GalleryDownloader.get(galleryBlock.id)) { + text = when(GalleryDownloader.get(gallery.id)) { null -> getString(R.string.reader_fab_download) else -> getString(R.string.reader_fab_download_cancel) } - isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(galleryBlock.id, false) + isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(gallery.id, false) setOnClickListener { - val downloader = GalleryDownloader.get(galleryBlock.id) + val downloader = GalleryDownloader.get(gallery.id) if (downloader == null) - GalleryDownloader(context, galleryBlock, true).start() + GalleryDownloader(context, gallery.id, true).start() else { downloader.cancel() downloader.clearNotification() @@ -420,16 +424,16 @@ class MainActivity : AppCompatActivity() { view.main_dialog_delete.setOnClickListener { CoroutineScope(Dispatchers.Default).launch { - with(GalleryDownloader[galleryBlock.id]) { + with(GalleryDownloader[gallery.id]) { this?.cancelAndJoin() this?.clearNotification() } - val cache = File(cacheDir, "imageCache/${galleryBlock.id}") - val data = getCachedGallery(context, galleryBlock.id) + val cache = File(cacheDir, "imageCache/${gallery.id}") + val data = getCachedGallery(context, gallery.id) cache.deleteRecursively() data.deleteRecursively() - downloads.remove(galleryBlock.id) + downloads.remove(gallery.id) if (mode == Mode.DOWNLOAD) { runOnUiThread { @@ -440,7 +444,7 @@ class MainActivity : AppCompatActivity() { } } - (adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false) + (adapter as GalleryBlockAdapter).completeFlag.put(gallery.id, false) } dialog.dismiss() } @@ -583,7 +587,7 @@ class MainActivity : AppCompatActivity() { //BOTTOM //Scrolling DOWN - if (dist < 0 && currentPage != Math.ceil(totalItems.toDouble()/perPage).roundToInt()-1) { + if (dist < 0 && currentPage != ceil(totalItems.toDouble()/perPage).roundToInt()-1) { with(main_recyclerview.adapter as GalleryBlockAdapter) { if(!showNext) { showNext = true @@ -595,7 +599,7 @@ class MainActivity : AppCompatActivity() { getChildAt(childCount-1) } - val absDist = Math.abs(dist) + val absDist = abs(dist) if (next is LinearLayout) { val icon = next.findViewById(R.id.icon_next) @@ -701,7 +705,7 @@ class MainActivity : AppCompatActivity() { setMessage(getString( R.string.main_jump_message, currentPage+1, - Math.ceil(totalItems / perPage.toDouble()).roundToInt() + ceil(totalItems / perPage.toDouble()).roundToInt() )) setPositiveButton(android.R.string.ok) { _, _ -> @@ -729,10 +733,7 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this@MainActivity, ReaderActivity::class.java) val gallery = getGalleryBlock(editText.text.toString().toInt()) ?: throw Exception() - intent.putExtra( - "galleryblock", - Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery) - ) + intent.putExtra("galleryID", gallery.id) startActivity(intent) } catch (e: Exception) { @@ -747,10 +748,17 @@ class MainActivity : AppCompatActivity() { } setOnQueryChangeListener { _, query -> + this@MainActivity.query = query + clearSuggestions() - if (query.isEmpty() or query.endsWith(' ')) + if (query.isEmpty() or query.endsWith(' ')) { + swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { + TagSuggestion(it.tag, -1, "", it.area ?: "tag") + }) + return@setOnQueryChangeListener + } val currentQuery = query.split(" ").last().replace('_', ' ') @@ -852,8 +860,6 @@ class MainActivity : AppCompatActivity() { delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length) append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ") } - - clearSuggestions() } override fun onSearchAction(currentQuery: String?) { @@ -863,7 +869,7 @@ class MainActivity : AppCompatActivity() { setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener { override fun onFocus() { - if (searchInputView.text.isEmpty()) + if (query.isEmpty() or query.endsWith(' ')) swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") }) diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index e29b3111..6bd102b9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.PagerSnapHelper @@ -21,13 +22,8 @@ import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.io.IOException import kotlinx.serialization.ImplicitReflectionSerializer -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonConfiguration -import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.getGalleryBlock import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.ReaderAdapter @@ -37,8 +33,8 @@ import xyz.quaver.pupil.util.ItemClickSupport class ReaderActivity : AppCompatActivity() { + private var galleryID = 0 private val images = ArrayList() - private lateinit var galleryBlock: GalleryBlock private var gallerySize = 0 private var currentPage = 0 @@ -66,6 +62,9 @@ class ReaderActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + title = getString(R.string.reader_loading) + supportActionBar?.setDisplayHomeAsUpEnabled(false) + favorites = (application as Pupil).favorites window.setFlags( @@ -76,16 +75,13 @@ class ReaderActivity : AppCompatActivity() { handleIntent(intent) - Crashlytics.setInt("GalleryID", galleryBlock.id) + Crashlytics.setInt("GalleryID", galleryID) - if (!::galleryBlock.isInitialized) { + if (galleryID == 0) { onBackPressed() return } - supportActionBar?.title = galleryBlock.title - supportActionBar?.setDisplayHomeAsUpEnabled(false) - initDownloader() initView() @@ -106,25 +102,16 @@ class ReaderActivity : AppCompatActivity() { if (uri != null && lastPathSegment != null) { val nonNumber = Regex("[^-?0-9]+") - val galleryID = when (uri.host) { + galleryID = when (uri.host) { "hitomi.la" -> lastPathSegment.replace(nonNumber, "").toInt() "히요비.asia" -> lastPathSegment.toInt() "xn--9w3b15m8vo.asia" -> lastPathSegment.toInt() "e-hentai.org" -> uri.pathSegments[1].toInt() else -> return } - - runBlocking { - CoroutineScope(Dispatchers.IO).launch { - galleryBlock = getGalleryBlock(galleryID) ?: return@launch - }.join() - } } } else { - galleryBlock = Json(JsonConfiguration.Stable).parse( - GalleryBlock.serializer(), - intent.getStringExtra("galleryblock")!! - ) + galleryID = intent.getIntExtra("galleryID", 0) } } @@ -148,7 +135,7 @@ class ReaderActivity : AppCompatActivity() { with(menu?.findItem(R.id.reader_menu_favorite)) { this ?: return@with - if (favorites.contains(galleryBlock.id)) + if (favorites.contains(galleryID)) (icon as Animatable).start() } @@ -176,7 +163,7 @@ class ReaderActivity : AppCompatActivity() { dialog.show() } R.id.reader_menu_favorite -> { - val id = galleryBlock.id + val id = galleryID val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true if (favorites.contains(id)) { @@ -215,11 +202,11 @@ class ReaderActivity : AppCompatActivity() { } private fun initDownloader() { - var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id) + var d: GalleryDownloader? = GalleryDownloader.get(galleryID) if (d == null) { try { - d = GalleryDownloader(this, galleryBlock) + d = GalleryDownloader(this, galleryID) } catch (e: IOException) { Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show() finish() @@ -230,17 +217,18 @@ class ReaderActivity : AppCompatActivity() { downloader = d.apply { onReaderLoadedHandler = { CoroutineScope(Dispatchers.Main).launch { + title = it.title with(reader_download_progressbar) { - max = it.size + max = it.readerItems.size progress = 0 } with(reader_progressbar) { - max = it.size + max = it.readerItems.size progress = 0 } - gallerySize = it.size - menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.size}" + gallerySize = it.readerItems.size + menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.readerItems.size}" } } onProgressHandler = { @@ -341,18 +329,24 @@ class ReaderActivity : AppCompatActivity() { } } - reader_fab_fullscreen.setOnClickListener { - isFullscreen = true - fullscreen(isFullscreen) + with(reader_fab_download) { + setImageResource(R.drawable.ic_download) + setOnClickListener { + downloader.download = !downloader.download - reader_fab.close(true) + if (!downloader.download) + downloader.clearNotification() + } } - reader_fab_download.setOnClickListener { - downloader.download = !downloader.download + with(reader_fab_fullscreen) { + setImageResource(R.drawable.ic_fullscreen) + setOnClickListener { + isFullscreen = true + fullscreen(isFullscreen) - if (!downloader.download) - downloader.clearNotification() + this@ReaderActivity.reader_fab.close(true) + } } } diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt index 9be00515..53a46770 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -13,12 +13,13 @@ import kotlinx.coroutines.* import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list -import xyz.quaver.hitomi.* +import xyz.quaver.hitomi.Reader +import xyz.quaver.hitomi.getReader +import xyz.quaver.hitomi.getReferer import xyz.quaver.hiyobi.cookie import xyz.quaver.hiyobi.user_agent -import xyz.quaver.pupil.R import xyz.quaver.pupil.Pupil +import xyz.quaver.pupil.R import xyz.quaver.pupil.ui.ReaderActivity import java.io.File import java.io.FileOutputStream @@ -30,7 +31,7 @@ import kotlin.concurrent.schedule class GalleryDownloader( base: Context, - private val galleryBlock: GalleryBlock, + private val galleryID: Int, _notify: Boolean = false ) : ContextWrapper(base) { @@ -41,10 +42,10 @@ class GalleryDownloader( set(value) { if (value) { field = true - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) - val data = getCachedGallery(this, galleryBlock.id) - val cache = File(cacheDir, "imageCache/${galleryBlock.id}") + val data = getCachedGallery(this, galleryID) + val cache = File(cacheDir, "imageCache/$galleryID") if (File(cache, "images").exists() && !data.exists()) { cache.copyRecursively(data, true) @@ -54,7 +55,7 @@ class GalleryDownloader( if (reader?.isActive == false && downloadJob?.isActive != true) field = false - downloads.add(galleryBlock.id) + downloads.add(galleryID) } else { field = false } @@ -78,24 +79,24 @@ class GalleryDownloader( companion object : SparseArray() init { - put(galleryBlock.id, this) + put(galleryID, this) initNotification() reader = CoroutineScope(Dispatchers.IO).async { download = _notify val json = Json(JsonConfiguration.Stable) - val serializer = ReaderItem.serializer().list + val serializer = Reader.serializer() //Check cache - val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "reader.json") + val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json") if (cache.exists()) { val cached = json.parse(serializer, cache.readText()) - if (cached.isNotEmpty()) { + if (cached.readerItems.isNotEmpty()) { useHiyobi = when { - cached.first().url.contains("hitomi.la") -> false + cached.readerItems[0].url.contains("hitomi.la") -> false else -> true } @@ -108,22 +109,22 @@ class GalleryDownloader( //Cache doesn't exist. Load from internet val reader = when { useHiyobi -> { - xyz.quaver.hiyobi.getReader(galleryBlock.id).let { + xyz.quaver.hiyobi.getReader(galleryID).let { when { - it.isEmpty() -> { + it.readerItems.isEmpty() -> { useHiyobi = false - getReader(galleryBlock.id) + getReader(galleryID) } else -> it } } } else -> { - getReader(galleryBlock.id) + getReader(galleryID) } } - if (reader.isNotEmpty()) { + if (reader.readerItems.isNotEmpty()) { //Save cache if (cache.parentFile?.exists() == false) cache.parentFile!!.mkdirs() @@ -141,7 +142,7 @@ class GalleryDownloader( downloadJob = CoroutineScope(Dispatchers.Default).launch { val reader = reader!!.await() - if (reader.isEmpty()) + if (reader.readerItems.isEmpty()) onErrorHandler?.invoke(IOException("Couldn't retrieve Reader")) val list = ArrayList() @@ -149,29 +150,20 @@ class GalleryDownloader( onReaderLoadedHandler?.invoke(reader) notificationBuilder - .setProgress(reader.size, 0, false) - .setContentText("0/${reader.size}") + .setProgress(reader.readerItems.size, 0, false) + .setContentText("0/${reader.readerItems.size}") - reader.chunked(4).forEachIndexed { chunkIndex, chunked -> + reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked -> chunked.mapIndexed { i, it -> val index = chunkIndex*4+i - onProgressHandler?.invoke(index) - - notificationBuilder - .setProgress(reader.size, index, false) - .setContentText("$index/${reader.size}") - - if (download) - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) - async(Dispatchers.IO) { val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url val name = "$index".padStart(4, '0') val ext = url.split('.').last() - val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "images/$name.$ext") + val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "images/$name.$ext") if (!cache.exists()) try { @@ -180,7 +172,7 @@ class GalleryDownloader( setRequestProperty("User-Agent", user_agent) setRequestProperty("Cookie", cookie) } else - setRequestProperty("Referer", getReferer(galleryBlock.id)) + setRequestProperty("Referer", getReferer(galleryID)) if (cache.parentFile?.exists() == false) cache.parentFile!!.mkdirs() @@ -193,31 +185,43 @@ class GalleryDownloader( onErrorHandler?.invoke(e) notificationBuilder - .setContentTitle(galleryBlock.title) + .setContentTitle(reader.title) .setContentText(getString(R.string.reader_notification_error)) .setProgress(0, 0, false) - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) } cache.absolutePath } }.forEach { list.add(it.await()) + + val index = list.size + + onProgressHandler?.invoke(index) + + notificationBuilder + .setProgress(reader.readerItems.size, index, false) + .setContentText("$index/${reader.readerItems.size}") + + if (download) + notificationManager.notify(galleryID, notificationBuilder.build()) + onDownloadedHandler?.invoke(list) } } Timer(false).schedule(1000) { notificationBuilder - .setContentTitle(galleryBlock.title) + .setContentTitle(reader.title) .setContentText(getString(R.string.reader_notification_complete)) .setProgress(0, 0, false) if (download) { - File(cacheDir, "imageCache/${galleryBlock.id}").let { + File(cacheDir, "imageCache/${galleryID}").let { if (it.exists()) { - val target = File(getDownloadDirectory(this@GalleryDownloader), galleryBlock.id.toString()) + val target = File(getDownloadDirectory(this@GalleryDownloader), galleryID.toString()) if (!target.exists()) target.mkdirs() @@ -227,7 +231,7 @@ class GalleryDownloader( } } - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) download = false } @@ -235,20 +239,20 @@ class GalleryDownloader( onCompleteHandler?.invoke() } - remove(galleryBlock.id) + remove(galleryID) } } fun cancel() { downloadJob?.cancel() - remove(galleryBlock.id) + remove(galleryID) } suspend fun cancelAndJoin() { downloadJob?.cancelAndJoin() - remove(galleryBlock.id) + remove(galleryID) } fun invokeOnReaderLoaded() { @@ -258,7 +262,7 @@ class GalleryDownloader( } fun clearNotification() { - notificationManager.cancel(galleryBlock.id) + notificationManager.cancel(galleryID) } fun invokeOnNotifyChanged() { @@ -267,22 +271,28 @@ class GalleryDownloader( private fun initNotification() { val intent = Intent(this, ReaderActivity::class.java).apply { - putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), galleryBlock)) + putExtra("galleryID", galleryID) } val pendingIntent = TaskStackBuilder.create(this).run { addNextIntentWithParentStack(intent) getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) } + notificationManager = NotificationManagerCompat.from(this) + notificationBuilder = NotificationCompat.Builder(this, "download").apply { - setContentTitle(galleryBlock.title) + setContentTitle(getString(R.string.reader_loading)) setContentText(getString(R.string.reader_notification_text)) setSmallIcon(R.drawable.ic_download) setContentIntent(pendingIntent) setProgress(0, 0, true) priority = NotificationCompat.PRIORITY_LOW } - notificationManager = NotificationManagerCompat.from(this) + + CoroutineScope(Dispatchers.Default).launch { + while (reader == null) ; + notificationBuilder.setContentTitle(reader.await().title) + } } } \ No newline at end of file diff --git a/app/src/main/res/layout/item_reader.xml b/app/src/main/res/layout/item_reader.xml index 9f5abe22..eb9909bd 100644 --- a/app/src/main/res/layout/item_reader.xml +++ b/app/src/main/res/layout/item_reader.xml @@ -3,6 +3,7 @@ android:contentDescription="@string/reader_imageview_description" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="100dp" android:paddingBottom="8dp" android:scaleType="fitCenter" android:adjustViewBounds="true"/> \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8ea7ac44..8e0c1660 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -79,4 +79,5 @@ ロックが一致しません。やり直してください。 なし ロックを無効にしますか? + ロード中 \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index a1fe7f96..8fe85791 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -79,4 +79,5 @@ 잠금이 일치하지 않습니다. 다시 시도하세요. 없음 잠금을 해제할까요? + 로딩중 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3a2738d3..3bfb2b16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -71,6 +71,7 @@ Type: %1$s Language: %1$s + Loading Go to page Fullscreen Background download diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt new file mode 100644 index 00000000..ef73d8da --- /dev/null +++ b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt @@ -0,0 +1,72 @@ +package xyz.quaver.hitomi + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import kotlinx.serialization.list +import org.jsoup.Jsoup +import xyz.quaver.hiyobi.HiyobiReader +import java.net.URL + +fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" +fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" + +fun webpReaderFromReader(reader: Reader) : Reader { + if (reader is HiyobiReader) + return reader + + return Reader(reader.title, reader.readerItems.map { + ReaderItem( + if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url, + it.galleryInfo + ) + }) +} + +@Serializable +data class GalleryInfo( + val width: Int, + val haswebp: Int, + val name: String, + val height: Int +) +@Serializable +data class ReaderItem( + val url: String, + val galleryInfo: GalleryInfo? +) + +@Serializable +open class Reader(val title: String, val readerItems: List) + +//Set header `Referer` to reader url to avoid 403 error +fun getReader(galleryID: Int) : Reader { + val readerUrl = "https://hitomi.la/reader/$galleryID.html" + val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" + + val doc = Jsoup.connect(readerUrl).get() + + val title = doc.title() + + val images = doc.select(".img-url").map { + protocol + urlFromURL(it.text()) + } + + val galleryInfo = ArrayList() + + galleryInfo.addAll( + Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, + Regex("""\[.+]""").find( + URL(galleryInfoUrl).readText() + )?.value ?: "[]" + ) + ) + + if (images.size > galleryInfo.size) + galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) + + return Reader(title, (images zip galleryInfo).map { + ReaderItem(it.first, it.second) + }) +} \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt deleted file mode 100644 index 0be173e9..00000000 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ /dev/null @@ -1,57 +0,0 @@ -package xyz.quaver.hitomi - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list -import org.jsoup.Jsoup -import java.net.URL - -fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" - -@Serializable -data class GalleryInfo( - val width: Int, - val haswebp: Int, - val name: String, - val height: Int -) -@Serializable -data class ReaderItem( - val url: String, - val galleryInfo: GalleryInfo? -) -typealias Reader = List -//Set header `Referer` to reader url to avoid 403 error -fun getReader(galleryID: Int) : Reader { - val readerUrl = "https://hitomi.la/reader/$galleryID.html" - val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" - - try { - val doc = Jsoup.connect(readerUrl).get() - - val images = doc.select(".img-url").map { - protocol + urlFromURL(it.text()) - } - - val galleryInfo = ArrayList() - - galleryInfo.addAll( - Json(JsonConfiguration.Stable).parse( - GalleryInfo.serializer().list, - Regex("""\[.+]""").find( - URL(galleryInfoUrl).readText() - )?.value ?: "[]" - ) - ) - - if (images.size > galleryInfo.size) - galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - - return (images zip galleryInfo).map { - ReaderItem(it.first, it.second) - } - } catch (e: Exception) { - return emptyList() - } -} \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index a20de850..e02983fe 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -1,9 +1,9 @@ package xyz.quaver.hiyobi -import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.content +import org.jsoup.Jsoup import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.ReaderItem import java.net.URL @@ -12,13 +12,15 @@ import javax.net.ssl.HttpsURLConnection const val hiyobi = "xn--9w3b15m8vo.asia" const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" -var cookie: String = "" -get() { - if (field.isEmpty()) - field = renewCookie() +class HiyobiReader(title: String, readerItems: List) : Reader(title, readerItems) - return field -} +var cookie: String = "" + get() { + if (field.isEmpty()) + field = renewCookie() + + return field + } fun renewCookie() : String { val url = "https://$hiyobi/" @@ -35,26 +37,25 @@ fun renewCookie() : String { } } -fun getReader(galleryId: Int) : Reader { - val url = "https://$hiyobi/data/json/${galleryId}_list.json" +fun getReader(galleryID: Int) : Reader { + val reader = "https://$hiyobi/reader/$galleryID" + val url = "https://$hiyobi/data/json/${galleryID}_list.json" - try { - val json = Json(JsonConfiguration.Stable).parseJson( - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - setRequestProperty("Cookie", cookie) - connectTimeout = 2000 - connect() + val title = Jsoup.connect(reader).get().title() - inputStream.bufferedReader().use { it.readText() } - } - ) + val json = Json(JsonConfiguration.Stable).parseJson( + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + setRequestProperty("Cookie", cookie) + connectTimeout = 2000 + connect() - return json.jsonArray.map { - val name = it.jsonObject["name"]!!.content - ReaderItem("https://$hiyobi/data/$galleryId/$name", null) + inputStream.bufferedReader().use { it.readText() } } - } catch (e: Exception) { - return emptyList() - } + ) + + return Reader(title, json.jsonArray.map { + val name = it.jsonObject["name"]!!.content + ReaderItem("https://$hiyobi/data/$galleryID/$name", null) + }) } \ No newline at end of file From 7e87bb6838ff8a7412fdff3ebf66a62520bea018 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Wed, 3 Jul 2019 19:40:19 +0900 Subject: [PATCH 2/5] Search algorithm improved Language settings in default tag fixed --- app/build.gradle | 2 +- .../main/java/xyz/quaver/pupil/types/Tags.kt | 4 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 162 +++++++++++------- .../xyz/quaver/pupil/ui/SettingsActivity.kt | 4 +- .../res/layout/item_selector_suggestion.xml | 34 ++++ .../main/java/xyz/quaver/hitomi/results.kt | 31 ++-- 6 files changed, 152 insertions(+), 85 deletions(-) create mode 100644 app/src/main/res/layout/item_selector_suggestion.xml diff --git a/app/build.gradle b/app/build.gradle index 9b1f14c2..db1cf65d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,7 @@ android { minSdkVersion 16 targetSdkVersion 29 versionCode 20 - versionName "2.11.1" + versionName "2.12" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt index 70d0a119..4d784f1e 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt @@ -90,8 +90,8 @@ class Tags(tag: List?) : ArrayList() { } } - fun removeByArea(area: String) { - filter { it.area == area }.forEach { + fun removeByArea(area: String, isNegative: Boolean? = null) { + filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach { remove(it) } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 084020b1..cfdf076d 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -17,6 +17,7 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.cardview.widget.CardView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @@ -45,6 +46,7 @@ import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.GalleryBlockAdapter +import xyz.quaver.pupil.types.SelectorSuggestion import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.TagSuggestion import xyz.quaver.pupil.types.Tags @@ -755,7 +757,7 @@ class MainActivity : AppCompatActivity() { if (query.isEmpty() or query.endsWith(' ')) { swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") - }) + } + SelectorSuggestion()) return@setOnQueryChangeListener } @@ -782,83 +784,115 @@ class MainActivity : AppCompatActivity() { } setOnBindSuggestionCallback { suggestionView, leftIcon, textView, item, _ -> - val suggestion = item as TagSuggestion - val tag = "${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")}" + if (item is SelectorSuggestion) { + var hasSelector = false - leftIcon.setImageDrawable( - ResourcesCompat.getDrawable( - resources, - when(suggestion.n) { - "female" -> R.drawable.ic_gender_female - "male" -> R.drawable.ic_gender_male - "language" -> R.drawable.ic_translate - "group" -> R.drawable.ic_account_group - "character" -> R.drawable.ic_account_star - "series" -> R.drawable.ic_book_open - "artist" -> R.drawable.ic_brush - else -> R.drawable.ic_tag - }, - null) - ) - - with(suggestionView.findViewById(R.id.right_icon)) { - - if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag)) - setImageResource(R.drawable.ic_star_filled) - else - setImageResource(R.drawable.ic_star_empty) - - visibility = View.VISIBLE - rotation = 0f - isEnabled = true - - setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500)) - - isClickable = true - setOnClickListener { - val favorites = Tags(json.parse(serializer, favoritesFile.readText())) - - if (favorites.contains(tag)) { - setImageResource(R.drawable.ic_star_empty) - favorites.remove(tag) + with(suggestionView as LinearLayout) { + for (i in 0 until childCount) { + val child = getChildAt(i) + if (child is ConstraintLayout) { + child.visibility = View.VISIBLE + hasSelector = true + } + else + child.visibility = View.GONE } - else { - setImageDrawable(AnimatedVectorDrawableCompat.create(context, - R.drawable.avd_star - )) - (drawable as Animatable).start() - - favorites.add(tag) - } - - favoritesFile.writeText(json.stringify(favorites)) } - } - if (suggestion.t == -1) { - textView.text = suggestion.s - } else { - val text = "${suggestion.s}\n ${suggestion.t}" + if (!hasSelector) { + val view = LayoutInflater.from(context) + .inflate(R.layout.item_selector_suggestion, suggestionView, false) - val len = text.length - val left = suggestion.s.length + suggestionView.addView(view) + } + } else if(item is TagSuggestion) { + with(suggestionView as LinearLayout) { + for (i in 0 until childCount) { + val child = getChildAt(i) + if (child is ConstraintLayout) { + child.visibility = View.GONE + } + else + child.visibility = View.VISIBLE + } + } + val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}" - textView.text = SpannableString(text).apply { - val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE) - setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + leftIcon.setImageDrawable( + ResourcesCompat.getDrawable( + resources, + when(item.n) { + "female" -> R.drawable.ic_gender_female + "male" -> R.drawable.ic_gender_male + "language" -> R.drawable.ic_translate + "group" -> R.drawable.ic_account_group + "character" -> R.drawable.ic_account_star + "series" -> R.drawable.ic_book_open + "artist" -> R.drawable.ic_brush + else -> R.drawable.ic_tag + }, + null) + ) + + with(suggestionView.findViewById(R.id.right_icon)) { + + if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag)) + setImageResource(R.drawable.ic_star_filled) + else + setImageResource(R.drawable.ic_star_empty) + + rotation = 0f + isEnabled = true + + setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500)) + + isClickable = true + setOnClickListener { + val favorites = Tags(json.parse(serializer, favoritesFile.readText())) + + if (favorites.contains(tag)) { + setImageResource(R.drawable.ic_star_empty) + favorites.remove(tag) + } + else { + setImageDrawable(AnimatedVectorDrawableCompat.create(context, + R.drawable.avd_star + )) + (drawable as Animatable).start() + + favorites.add(tag) + } + + favoritesFile.writeText(json.stringify(favorites)) + } + } + + if (item.t == -1) { + textView.text = item.s + } else { + val text = "${item.s}\n ${item.t}" + + val len = text.length + val left = item.s.length + + textView.text = SpannableString(text).apply { + val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE) + setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } } } } setOnSearchListener(object : FloatingSearchView.OnSearchListener { override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) { - val suggestion = searchSuggestion as TagSuggestion + if (searchSuggestion !is TagSuggestion) + return with(searchInputView.text) { delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length) - append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ") + append("${searchSuggestion.n}:${searchSuggestion.s.replace(Regex("\\s"), "_")} ") } } @@ -872,7 +906,7 @@ class MainActivity : AppCompatActivity() { if (query.isEmpty() or query.endsWith(' ')) swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") - }) + } + SelectorSuggestion()) } override fun onFocusCleared() { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index e4229365..ea0d9f8c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -222,14 +222,14 @@ class SettingsActivity : AppCompatActivity() { addAll(languages.values) } ) - if (tags.any { it.area == "language" }) { + if (tags.any { it.area == "language" && !it.isNegative }) { val tag = languages[tags.first { it.area == "language" }.tag] if (tag != null) { setSelection( @Suppress("UNCHECKED_CAST") (adapter as ArrayAdapter).getPosition(tag) ) - tags.removeByArea("language") + tags.removeByArea("language", false) } } } diff --git a/app/src/main/res/layout/item_selector_suggestion.xml b/app/src/main/res/layout/item_selector_suggestion.xml new file mode 100644 index 00000000..144792cb --- /dev/null +++ b/app/src/main/res/layout/item_selector_suggestion.xml @@ -0,0 +1,34 @@ + + + + + + + +