From 24aedfc400367339f66ba48423c25b7c7c691c36 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 27 Nov 2020 20:44:41 +0900 Subject: [PATCH] Minor fix --- app/build.gradle | 11 +- app/proguard-rules.pro | 17 +- .../quaver/pupil/ExampleInstrumentedTest.kt | 1 + app/src/main/AndroidManifest.xml | 8 +- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 6 +- .../quaver/pupil/adapters/ReaderAdapter.kt | 121 +----------- .../pupil/adapters/SearchResultsAdapter.kt | 122 +++++++++++-- .../java/xyz/quaver/pupil/sources/Common.kt | 41 +++-- .../xyz/quaver/pupil/sources/hitomi/Hitomi.kt | 112 +++++++++--- .../xyz/quaver/pupil/sources/hitomi/Hiyobi.kt | 69 +++++++ .../java/xyz/quaver/pupil/ui/MainActivity.kt | 86 +++++---- .../xyz/quaver/pupil/ui/ReaderActivity.kt | 172 ------------------ ...ialog.kt => DefaultQueryDialogFragment.kt} | 73 ++++---- .../quaver/pupil/ui/dialog/GalleryDialog.kt | 15 +- .../pupil/ui/fragment/SettingsFragment.kt | 4 +- .../java/xyz/quaver/pupil/ui/view/TagChip.kt | 10 +- .../main/java/xyz/quaver/pupil/util/camera.kt | 119 ------------ .../main/res/layout/progress_card_view.xml | 2 +- app/src/main/res/layout/reader_item.xml | 2 +- .../main/res/layout/search_result_item.xml | 32 +--- app/src/main/res/values/arrays.xml | 40 ---- app/src/main/res/values/strings.xml | 2 +- gradle.properties | 1 + 23 files changed, 426 insertions(+), 640 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hiyobi.kt rename app/src/main/java/xyz/quaver/pupil/ui/dialog/{DefaultQueryDialog.kt => DefaultQueryDialogFragment.kt} (73%) delete mode 100644 app/src/main/java/xyz/quaver/pupil/util/camera.kt diff --git a/app/build.gradle b/app/build.gradle index c057f840..9b296214 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,13 +94,12 @@ dependencies { implementation "com.google.android.material:material:1.3.0-alpha04" - implementation "com.google.firebase:firebase-core:18.0.0" - implementation "com.google.firebase:firebase-analytics:18.0.0" - implementation "com.google.firebase:firebase-crashlytics:17.3.0" - implementation "com.google.firebase:firebase-perf:19.0.10" + implementation platform("com.google.firebase:firebase-bom:26.1.0") + implementation "com.google.firebase:firebase-analytics-ktx" + implementation "com.google.firebase:firebase-crashlytics" + implementation "com.google.firebase:firebase-perf" implementation "com.google.android.gms:play-services-oss-licenses:17.0.0" - implementation "com.google.android.gms:play-services-mlkit-face-detection:16.1.1" implementation "com.github.clans:fab:1.6.4" @@ -123,7 +122,7 @@ dependencies { implementation "ru.noties.markwon:core:3.1.0" - implementation "xyz.quaver:libpupil:1.9.0" + implementation "xyz.quaver:libpupil:1.9.7" implementation "xyz.quaver:documentfilex:0.4-alpha02" implementation "xyz.quaver:floatingsearchview:1.0.7" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7b1b98e0..63a3a4b6 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -23,12 +23,21 @@ -dontobfuscate -keepattributes *Annotation*, InnerClasses --dontnote kotlinx.serialization.SerializationKt --keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's --keepclassmembers class xyz.quaver.pupil.** { # <-- change package name to your app's +-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations + +# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer +-keepclassmembers class kotlinx.serialization.json.** { *** Companion; } --keepclasseswithmembers class xyz.quaver.pupil.** { # <-- change package name to your app's +-keepclasseswithmembers class kotlinx.serialization.json.** { + kotlinx.serialization.KSerializer serializer(...); +} + +-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } +-keepclassmembers class xyz.quaver.** { + *** Companion; +} +-keepclasseswithmembers class xyz.quaver.** { kotlinx.serialization.KSerializer serializer(...); } -keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index b021026c..26ecdbf8 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -20,6 +20,7 @@ package xyz.quaver.pupil +import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import org.junit.Test diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d5e1e304..3675b4b6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,15 +6,11 @@ - - + + - - - - - binding.image.updateLayoutParams { - dimensionRatio = "${imageInfo.width}:${imageInfo.height}" - } - } - }) - setImageShownCallback(object : ImageShownCallback { - override fun onMainImageShown() { - binding.image.mainView.let { v -> - when (v) { - is SubsamplingScaleImageView -> - if (!isFullScreen) binding.image.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT - } - } - } - - override fun onThumbnailShown() {} - }) - setFailureImage(ContextCompat.getDrawable(itemView.context, R.drawable.image_broken_variant)) setOnClickListener { onItemClickListener?.invoke() @@ -164,87 +134,4 @@ class ReaderAdapter( holder.clear() } -} - -class FrescoImageViewFactory : ImageViewFactory() { - var updateView: ((ImageInfo) -> Unit)? = null - - override fun createAnimatedImageView( - context: Context, imageType: Int, - initScaleType: Int - ): View { - val view = SimpleDraweeView(context) - view.hierarchy.actualImageScaleType = scaleType(initScaleType) - return view - } - - override fun loadAnimatedContent( - view: View, imageType: Int, - imageFile: File - ) { - if (view is SimpleDraweeView) { - val controller: DraweeController = Fresco.newDraweeControllerBuilder() - .setUri(Uri.parse("file://" + imageFile.absolutePath)) - .setAutoPlayAnimations(true) - .setControllerListener(object: BaseControllerListener() { - override fun onIntermediateImageSet(id: String?, imageInfo: ImageInfo?) { - imageInfo?.let { updateView?.invoke(it) } - } - - override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) { - imageInfo?.let { updateView?.invoke(it) } - } - }) - .build() - view.controller = controller - } - } - - override fun createThumbnailView( - context: Context, - scaleType: ImageView.ScaleType, willLoadFromNetwork: Boolean - ): View { - return if (willLoadFromNetwork) { - val thumbnailView = SimpleDraweeView(context) - thumbnailView.hierarchy.actualImageScaleType = scaleType(scaleType) - thumbnailView - } else { - super.createThumbnailView(context, scaleType, false) - } - } - - override fun loadThumbnailContent(view: View, thumbnail: Uri) { - if (view is SimpleDraweeView) { - val controller: DraweeController = Fresco.newDraweeControllerBuilder() - .setUri(thumbnail) - .build() - view.controller = controller - } - } - - private fun scaleType(value: Int): ScalingUtils.ScaleType { - return when (value) { - BigImageView.INIT_SCALE_TYPE_CENTER -> ScalingUtils.ScaleType.CENTER - BigImageView.INIT_SCALE_TYPE_CENTER_CROP -> ScalingUtils.ScaleType.CENTER_CROP - BigImageView.INIT_SCALE_TYPE_CENTER_INSIDE -> ScalingUtils.ScaleType.CENTER_INSIDE - BigImageView.INIT_SCALE_TYPE_FIT_END -> ScalingUtils.ScaleType.FIT_END - BigImageView.INIT_SCALE_TYPE_FIT_START -> ScalingUtils.ScaleType.FIT_START - BigImageView.INIT_SCALE_TYPE_FIT_XY -> ScalingUtils.ScaleType.FIT_XY - BigImageView.INIT_SCALE_TYPE_FIT_CENTER -> ScalingUtils.ScaleType.FIT_CENTER - else -> ScalingUtils.ScaleType.FIT_CENTER - } - } - - private fun scaleType(scaleType: ImageView.ScaleType): ScalingUtils.ScaleType { - return when (scaleType) { - ImageView.ScaleType.CENTER -> ScalingUtils.ScaleType.CENTER - ImageView.ScaleType.CENTER_CROP -> ScalingUtils.ScaleType.CENTER_CROP - ImageView.ScaleType.CENTER_INSIDE -> ScalingUtils.ScaleType.CENTER_INSIDE - ImageView.ScaleType.FIT_END -> ScalingUtils.ScaleType.FIT_END - ImageView.ScaleType.FIT_START -> ScalingUtils.ScaleType.FIT_START - ImageView.ScaleType.FIT_XY -> ScalingUtils.ScaleType.FIT_XY - ImageView.ScaleType.FIT_CENTER -> ScalingUtils.ScaleType.FIT_CENTER - else -> ScalingUtils.ScaleType.FIT_CENTER - } - } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt index af341930..b852ed47 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/SearchResultsAdapter.kt @@ -21,18 +21,19 @@ package xyz.quaver.pupil.adapters import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.net.Uri +import android.graphics.drawable.Animatable import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.recyclerview.widget.RecyclerView import com.daimajia.swipe.SwipeLayout import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.interfaces.SwipeAdapterInterface -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.controller.BaseControllerListener +import com.facebook.imagepipeline.image.ImageInfo +import kotlinx.coroutines.* import xyz.quaver.pupil.R import xyz.quaver.pupil.databinding.SearchResultItemBinding import xyz.quaver.pupil.sources.SearchResult @@ -40,21 +41,25 @@ import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.ui.view.ProgressCardView import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.DownloadManager -import xyz.quaver.pupil.util.wordCapitalize +import kotlin.time.ExperimentalTime class SearchResultsAdapter(private val results: List) : RecyclerSwipeAdapter(), SwipeAdapterInterface { - val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() + var onChipClickedHandler: ((Tag) -> Unit)? = null var onDownloadClickedHandler: ((String) -> Unit)? = null var onDeleteClickedHandler: ((String) -> Unit)? = null + // TODO: migrate to viewBinding + val progressUpdateScope = CoroutineScope(Dispatchers.Main + Job()) + inner class ViewHolder(private val binding: SearchResultItemBinding) : RecyclerView.ViewHolder(binding.root) { var itemID: String = "" - var update = true + + private var bindJob: Job? = null init { - CoroutineScope(Dispatchers.Main).launch { - while (update) { + progressUpdateScope.launch { + while (true) { updateProgress() delay(1000) } @@ -92,9 +97,11 @@ class SearchResultsAdapter(private val results: List) : RecyclerSw override fun onHandRelease(layout: SwipeLayout?, xvel: Float, yvel: Float) {} override fun onUpdate(layout: SwipeLayout?, leftOffset: Int, topOffset: Int) {} }) + + binding.tagGroup.onClickListener = onChipClickedHandler } - fun updateProgress() = CoroutineScope(Dispatchers.Main).launch { + private fun updateProgress() = CoroutineScope(Dispatchers.Main).launch { with (itemView as ProgressCardView) { val imageList = Cache.getInstance(context, itemID).metadata.imageList @@ -118,32 +125,111 @@ class SearchResultsAdapter(private val results: List) : RecyclerSw } } + val controllerListener = object: BaseControllerListener() { + override fun onIntermediateImageSet(id: String?, imageInfo: ImageInfo?) { + imageInfo?.let { + binding.thumbnail.aspectRatio = it.width / it.height.toFloat() + } + } + + override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) { + imageInfo?.let { + binding.thumbnail.aspectRatio = it.width / it.height.toFloat() + } + } + } fun bind(result: SearchResult) { + bindJob?.cancel() itemID = result.id - binding.thumbnail.ssiv?.recycle() - binding.thumbnail.showImage(Uri.parse(result.thumbnail)) + binding.thumbnail.controller = Fresco.newDraweeControllerBuilder() + .setUri(result.thumbnail) + .setControllerListener(controllerListener) + .build() updateProgress() binding.title.text = result.title binding.idView.text = result.id - binding.artist.text = result.artists.joinToString { it.wordCapitalize() } + + binding.artist.visibility = if (result.artists.isEmpty()) View.GONE else View.VISIBLE + binding.artist.text = result.artists + + with (binding.tagGroup) { + tags.clear() + tags.addAll(result.tags.map { + Tag.parse(it) + }) + refresh() + } + + binding.pagecount.text = "-" + + bindJob = MainScope().launch { + val extra = result.extra.mapValues { + async(Dispatchers.IO) { + kotlin.runCatching { withTimeout(1000) { + it.value.invoke() + } }.getOrNull() + } + } + + launch { + val extraType = listOf( + SearchResult.ExtraType.SERIES, + SearchResult.ExtraType.TYPE, + SearchResult.ExtraType.LANGUAGE + ) + + binding.extra.text = extra.entries.filter { it.key in extraType }.fold(StringBuilder()) { res, entry -> + entry.value.await().let { + if (!it.isNullOrEmpty()) { + res.append( + itemView.context.getString( + SearchResult.extraTypeMap[entry.key] ?: error(""), + it + ) + ) + res.append('\n') + } + res + } + } + } + + launch { + extra[SearchResult.ExtraType.PAGECOUNT]?.await()?.let { + binding.pagecount.text = + itemView.context.getString( + SearchResult.extraTypeMap[SearchResult.ExtraType.PAGECOUNT] ?: error(""), + it + ) + } + } + + launch { + extra[SearchResult.ExtraType.GROUP]?.await().let { + if (!it.isNullOrEmpty()) + binding.artist.text = itemView.context.getString( + R.string.galleryblock_artist_with_group, + result.artists, + it + ) + } + } + } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(SearchResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + @ExperimentalTime override fun onBindViewHolder(holder: ViewHolder, position: Int) { mItemManger.bindView(holder.itemView, position) holder.bind(results[position]) } - override fun onViewDetachedFromWindow(holder: ViewHolder) { - holder.update = false - } - override fun getItemCount(): Int = results.size override fun getSwipeLayoutResourceId(position: Int): Int = R.id.swipe_layout diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt index f7bf95c7..e1a5f0fb 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt @@ -18,20 +18,39 @@ package xyz.quaver.pupil.sources -import com.google.android.gms.vision.L +import kotlinx.coroutines.channels.Channel +import xyz.quaver.pupil.R import kotlin.reflect.KClass -interface SearchResult { - val id: String - val title: String - val thumbnail: String - val artists: List +data class SearchResult( + val id: String, + val title: String, + val thumbnail: String, + val artists: String, + val extra: Map String>, + val tags: List +) { + enum class ExtraType { + GROUP, + CHARACTER, + SERIES, + TYPE, + LANGUAGE, + PAGECOUNT + } + + companion object { + val extraTypeMap = mapOf( + ExtraType.SERIES to R.string.galleryblock_series, + ExtraType.TYPE to R.string.galleryblock_type, + ExtraType.LANGUAGE to R.string.galleryblock_language, + ExtraType.PAGECOUNT to R.string.galleryblock_pagecount + ) + } } -// Might be better to use channel on Query_Result -interface Source, Query_Result: SearchResult> { - val querySortModeClass: KClass - val queryResultClass: KClass +interface Source> { + val querySortModeClass: KClass? - suspend fun query(query: String, range: IntRange, sortMode: Query_SortMode? = null) : Pair, Int> + suspend fun query(query: String, range: IntRange, sortMode: Query_SortMode? = null) : Pair, Int> } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt index b7516fdc..98d9b097 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt @@ -18,35 +18,31 @@ package xyz.quaver.pupil.sources.hitomi -import kotlinx.coroutines.yield -import xyz.quaver.hitomi.doSearch -import xyz.quaver.hitomi.getGalleryBlock +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import xyz.quaver.hitomi.* +import xyz.quaver.pupil.sources.SearchResult +import xyz.quaver.pupil.sources.SearchResult.ExtraType import xyz.quaver.pupil.sources.Source -import kotlin.math.min +import xyz.quaver.pupil.util.wordCapitalize import kotlin.math.max +import kotlin.math.min -class Hitomi : Source { +class Hitomi : Source { override val querySortModeClass = SortMode::class - override val queryResultClass = SearchResult::class enum class SortMode { NEWEST, POPULAR } - data class SearchResult( - override val id: String, - override val title: String, - override val thumbnail: String, - override val artists: List, - ) : xyz.quaver.pupil.sources.SearchResult - var cachedQuery: String? = null + var cachedSortMode: SortMode? = null val cache = mutableListOf() - override suspend fun query(query: String, range: IntRange, sortMode: SortMode?): Pair, Int> { - if (cachedQuery != query) { + override suspend fun query(query: String, range: IntRange, sortMode: SortMode?): Pair, Int> { + if (cachedQuery != query || cachedSortMode != sortMode || cache.isEmpty()) { cachedQuery = null cache.clear() yield() @@ -57,17 +53,85 @@ class Hitomi : Source { cachedQuery = query } + val channel = Channel() val sanitizedRange = max(0, range.first) .. min(range.last, cache.size-1) - return Pair(cache.slice(sanitizedRange).map { - getGalleryBlock(it).let { gallery -> - SearchResult( - gallery.id.toString(), - gallery.title, - gallery.thumbnails.first(), - gallery.artists - ) + + CoroutineScope(Dispatchers.IO).launch { + cache.slice(sanitizedRange).map { + async { + getGalleryBlock(it) + } + }.forEach { + kotlin.runCatching { + yield() + channel.send(transform(it.await())) + }.onFailure { + channel.close() + } } - }, cache.size) + + channel.close() + } + + return Pair(channel, cache.size) + } + + companion object { + val languageMap = mapOf( + "indonesian" to "Bahasa Indonesia", + "catalan" to "català", + "cebuano" to "Cebuano", + "czech" to "Čeština", + "danish" to "Dansk", + "german" to "Deutsch", + "estonian" to "eesti", + "english" to "English", + "spanish" to "Español", + "esperanto" to "Esperanto", + "french" to "Français", + "italian" to "Italiano", + "latin" to "Latina", + "hungarian" to "magyar", + "dutch" to "Nederlands", + "norwegian" to "norsk", + "polish" to "polski", + "portuguese" to "Português", + "romanian" to "română", + "albanian" to "shqip", + "slovak" to "Slovenčina", + "finnish" to "Suomi", + "swedish" to "Svenska", + "tagalog" to "Tagalog", + "vietnamese" to "tiếng việt", + "turkish" to "Türkçe", + "greek" to "Ελληνικά", + "mongolian" to "Монгол", + "russian" to "Русский", + "ukrainian" to "Українська", + "hebrew" to "עברית", + "arabic" to "العربية", + "persian" to "فارسی", + "thai" to "ไทย", + "korean" to "한국어", + "chinese" to "中文", + "japanese" to "日本語" + ) + + fun transform(galleryBlock: GalleryBlock): SearchResult = + SearchResult( + galleryBlock.id.toString(), + galleryBlock.title, + galleryBlock.thumbnails.first(), + galleryBlock.artists.joinToString { it.wordCapitalize() }, + mapOf( + ExtraType.GROUP to { getGallery(galleryBlock.id).groups.joinToString { it.wordCapitalize() } }, + ExtraType.SERIES to { galleryBlock.series.joinToString { it.wordCapitalize() } }, + ExtraType.TYPE to { galleryBlock.type.wordCapitalize() }, + ExtraType.LANGUAGE to { languageMap[galleryBlock.language] ?: galleryBlock.language }, + ExtraType.PAGECOUNT to { getGalleryInfo(galleryBlock.id).files.size.toString() } + ), + galleryBlock.relatedTags + ) } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hiyobi.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hiyobi.kt new file mode 100644 index 00000000..cd62be61 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hiyobi.kt @@ -0,0 +1,69 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2020 tom5079 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package xyz.quaver.pupil.sources.hitomi + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import xyz.quaver.hiyobi.* +import xyz.quaver.pupil.sources.SearchResult +import xyz.quaver.pupil.sources.Source +import xyz.quaver.pupil.util.wordCapitalize + +class Hiyobi : Source> { + override val querySortModeClass = null + + override suspend fun query(query: String, range: IntRange, sortMode: Enum<*>?): Pair, Int> { + val channel = Channel() + + val (results, total) = if (query.isEmpty()) + list(range) + else + search(query, range) + + CoroutineScope(Dispatchers.Unconfined).launch { + results.forEach { + channel.send(transform(it)) + } + + channel.close() + } + + return Pair(channel, total) + } + + companion object { + fun transform(galleryBlock: GalleryBlock): SearchResult = + SearchResult( + galleryBlock.id, + galleryBlock.title, + "https://cdn.$hiyobi/tn/${galleryBlock.id}.jpg", + galleryBlock.artists.joinToString { it.value.wordCapitalize() }, + mapOf( + SearchResult.ExtraType.CHARACTER to { galleryBlock.characters.joinToString { it.value.wordCapitalize() } }, + SearchResult.ExtraType.SERIES to { galleryBlock.parodys.joinToString { it.value.wordCapitalize() } }, + SearchResult.ExtraType.TYPE to { galleryBlock.type.name.wordCapitalize() }, + SearchResult.ExtraType.PAGECOUNT to { getGalleryInfo(galleryBlock.id).files.size.toString() } + ), + galleryBlock.tags.map { it.value } + ) + } + +} \ No newline at end of file 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 4b588937..2b04d51c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -38,6 +38,7 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.navigation.NavigationView import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel import xyz.quaver.floatingsearchview.FloatingSearchView import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion import xyz.quaver.floatingsearchview.util.view.MenuView @@ -50,6 +51,7 @@ import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.sources.SearchResult import xyz.quaver.pupil.sources.Source import xyz.quaver.pupil.sources.hitomi.Hitomi +import xyz.quaver.pupil.sources.hitomi.Hiyobi import xyz.quaver.pupil.types.* import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment import xyz.quaver.pupil.ui.dialog.GalleryDialog @@ -62,6 +64,7 @@ import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.restore import java.util.regex.Pattern import kotlin.math.* +import kotlin.random.Random class MainActivity : BaseActivity(), @@ -80,10 +83,10 @@ class MainActivity : private var queryStack = mutableListOf() @Suppress("UNCHECKED_CAST") - private var source: Source, SearchResult> = Hitomi() as Source, SearchResult> + private var source: Source> = Hiyobi() as Source> private var sortMode = Hitomi.SortMode.NEWEST - private var searchJob: Deferred, Int>>? = null + private var searchJob: Deferred, Int>>? = null private var totalItems = 0 private var currentPage = 1 @@ -114,6 +117,12 @@ class MainActivity : initView() } + override fun onDestroy() { + super.onDestroy() + + (binding.contents.recyclerview.adapter as SearchResultsAdapter).progressUpdateScope.cancel() + } + @OptIn(ExperimentalStdlibApi::class) override fun onBackPressed() { when { @@ -215,25 +224,29 @@ class MainActivity : with(binding.contents.randomFab) { setImageResource(R.drawable.shuffle_variant) setOnClickListener { - runBlocking { - withTimeoutOrNull(100) { - searchJob?.await() - } - }.let { - if (it?.first?.isEmpty() == false) { - val random = it.first.random() + if (totalItems > 0) + CoroutineScope(Dispatchers.IO).launch { + val random = Random.Default.nextInt(totalItems) - GalleryDialog(this@MainActivity, random.id).apply { - onChipClickedHandler.add { - query = it.toQuery() - currentPage = 1 + val randomResult = + source.query( + query + Preferences["default_query", ""], + random .. random, + sortMode + ).first.receive() - query() - dismiss() - } - }.show() + launch(Dispatchers.Main) { + GalleryDialog(this@MainActivity, randomResult.id).apply { + onChipClickedHandler.add { + query = it.toQuery() + currentPage = 1 + + query() + dismiss() + } + }.show() + } } - } } } @@ -273,11 +286,6 @@ class MainActivity : // disable pageturn until the contents are loaded setCurrentPage(1, false) - ViewCompat.animate(binding.contents.searchview) - .setDuration(100) - .setInterpolator(DecelerateInterpolator()) - .translationY(0F) - query() } @@ -306,9 +314,9 @@ class MainActivity : private fun setupRecyclerView() { with(binding.contents.recyclerview) { adapter = SearchResultsAdapter(searchResults).apply { - onChipClickedHandler.add { + onChipClickedHandler = { query = it.toQuery() - currentPage = 0 + currentPage = 1 query() } @@ -353,7 +361,7 @@ class MainActivity : GalleryDialog(this@MainActivity, result.id).apply { onChipClickedHandler.add { query = it.toQuery() - currentPage = 0 + currentPage = 1 query() dismiss() @@ -535,6 +543,11 @@ class MainActivity : binding.contents.noresult.visibility = View.INVISIBLE binding.contents.progressbar.show() + + ViewCompat.animate(binding.contents.searchview) + .setDuration(100) + .setInterpolator(DecelerateInterpolator()) + .translationY(0F) } private fun query() { val perPage = Preferences["per_page", "25"].toInt() @@ -550,22 +563,21 @@ class MainActivity : sortMode ) }.also { - val results: List - it.await().let { r -> - results = r.first totalItems = r.second + r.first + }.let { channel -> + binding.contents.progressbar.hide() + binding.contents.swipePageTurnView.setCurrentPage(currentPage, totalItems > currentPage*perPage) + + for (result in channel) { + searchResults.add(result) + binding.contents.recyclerview.adapter?.notifyItemInserted(searchResults.size) + } } - binding.contents.progressbar.hide() - binding.contents.swipePageTurnView.setCurrentPage(currentPage, totalItems > currentPage*perPage) - - if (results.isEmpty()) { + if (searchResults.isEmpty()) binding.contents.noresult.visibility = View.VISIBLE - } else { - searchResults.addAll(results) - binding.contents.recyclerview.adapter?.notifyDataSetChanged() - } } } } 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 6570ce5e..7c1bc4cf 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -18,22 +18,14 @@ package xyz.quaver.pupil.ui -import android.Manifest import android.content.ComponentName import android.content.Intent import android.content.ServiceConnection -import android.content.pm.PackageManager import android.graphics.drawable.Animatable import android.graphics.drawable.Drawable -import android.os.Build import android.os.Bundle import android.os.IBinder import android.view.* -import android.view.animation.Animation -import android.view.animation.AnticipateInterpolator -import android.view.animation.OvershootInterpolator -import android.view.animation.TranslateAnimation -import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager @@ -43,7 +35,6 @@ import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.google.android.material.snackbar.Snackbar import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.google.mlkit.vision.face.Face import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -56,11 +47,8 @@ import xyz.quaver.pupil.databinding.ReaderActivityBinding import xyz.quaver.pupil.favorites import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.util.Preferences -import xyz.quaver.pupil.util.camera -import xyz.quaver.pupil.util.closeCamera import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.DownloadManager -import xyz.quaver.pupil.util.startCamera class ReaderActivity : BaseActivity() { @@ -95,26 +83,6 @@ class ReaderActivity : BaseActivity() { private val snapHelper = PagerSnapHelper() private var menu: Menu? = null - private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> - if (isGranted) - toggleCamera() - else - AlertDialog.Builder(this) - .setTitle(R.string.error) - .setMessage(R.string.camera_denied) - .setPositiveButton(android.R.string.ok) { _, _ ->} - .show() - } - - enum class Eye { - LEFT, - RIGHT - } - - private var cameraEnabled = false - private var eyeType: Eye? = null - private var eyeTime: Long = 0L - private lateinit var binding: ReaderActivityBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -217,14 +185,10 @@ class ReaderActivity : BaseActivity() { super.onResume() bindService(Intent(this, DownloadService::class.java), conn, BIND_AUTO_CREATE) - - if (cameraEnabled) - startCamera(this, cameraCallback) } override fun onPause() { super.onPause() - closeCamera() if (downloader != null) unbindService(conn) @@ -391,26 +355,6 @@ class ReaderActivity : BaseActivity() { } } - with(binding.autoFab) { - setImageResource(R.drawable.eye_white) - setOnClickListener { - when { - ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> { - toggleCamera() - } - Build.VERSION.SDK_INT >= 23 && shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> { - AlertDialog.Builder(this@ReaderActivity) - .setTitle(R.string.warning) - .setMessage(R.string.camera_denied) - .setPositiveButton(android.R.string.ok) { _, _ ->} - .show() - } - else -> - requestPermissionLauncher.launch(Manifest.permission.CAMERA) - } - } - } - with(binding.fullscreenFab) { setImageResource(R.drawable.ic_fullscreen) setOnClickListener { @@ -496,120 +440,4 @@ class ReaderActivity : BaseActivity() { } } } - - val cameraCallback: (List) -> Unit = callback@{ faces -> - binding.eyeCard.dot.let { - it.visibility = View.VISIBLE - CoroutineScope(Dispatchers.Main).launch { - delay(50) - it.visibility = View.INVISIBLE - } - } - - if (faces.size != 1) - ContextCompat.getDrawable(this, R.drawable.eye_off).let { - with(binding.eyeCard) { - leftEye.setImageDrawable(it) - rightEye.setImageDrawable(it) - } - - return@callback - } - - val (left, right) = Pair( - faces[0].rightEyeOpenProbability?.let { it > 0.4 } == true, - faces[0].leftEyeOpenProbability?.let { it > 0.4 } == true - ) - - with(binding.eyeCard) { - leftEye.setImageDrawable( - ContextCompat.getDrawable( - leftEye.context, - if (left) R.drawable.eye else R.drawable.eye_closed - ) - ) - rightEye.setImageDrawable( - ContextCompat.getDrawable( - rightEye.context, - if (right) R.drawable.eye else R.drawable.eye_closed - ) - ) - } - - when { - // Both closed / opened - !left.xor(right) -> { - eyeType = null - eyeTime = 0L - } - !left -> { - if (eyeType != Eye.LEFT) { - eyeType = Eye.LEFT - eyeTime = System.currentTimeMillis() - } - } - !right -> { - if (eyeType != Eye.RIGHT) { - eyeType = Eye.RIGHT - eyeTime = System.currentTimeMillis() - } - } - } - - if (eyeType != null && System.currentTimeMillis() - eyeTime > 100) { - (binding.recyclerview.layoutManager as LinearLayoutManager).let { - it.scrollToPositionWithOffset(when(eyeType!!) { - Eye.RIGHT -> { - if (it.reverseLayout) currentPage - 2 else currentPage - } - Eye.LEFT -> { - if (it.reverseLayout) currentPage else currentPage - 2 - } - }, 0) - } - - eyeTime = System.currentTimeMillis() + 500 - } - } - - private fun toggleCamera() { - val eyes = binding.eyeCard.root - when (camera) { - null -> { - binding.autoFab.labelText = getString(R.string.reader_fab_auto_cancel) - binding.autoFab.setImageResource(R.drawable.eye_off_white) - eyes.apply { - visibility = View.VISIBLE - TranslateAnimation(0F, 0F, -100F, 0F).apply { - duration = 500 - fillAfter = false - interpolator = OvershootInterpolator() - }.let { startAnimation(it) } - } - startCamera(this, cameraCallback) - cameraEnabled = true - } - else -> { - binding.autoFab.labelText = getString(R.string.reader_fab_auto) - binding.autoFab.setImageResource(R.drawable.eye_white) - eyes.apply { - TranslateAnimation(0F, 0F, 0F, -100F).apply { - duration = 500 - fillAfter = false - interpolator = AnticipateInterpolator() - setAnimationListener(object: Animation.AnimationListener { - override fun onAnimationStart(p0: Animation?) {} - override fun onAnimationRepeat(p0: Animation?) {} - - override fun onAnimationEnd(p0: Animation?) { - eyes.visibility = View.GONE - } - }) - }.let { startAnimation(it) } - } - closeCamera() - cameraEnabled = false - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt similarity index 73% rename from app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt rename to app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt index cb73f790..eeb76ecc 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialog.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DefaultQueryDialogFragment.kt @@ -19,24 +19,21 @@ package xyz.quaver.pupil.ui.dialog import android.app.Dialog -import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import xyz.quaver.pupil.R import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding +import xyz.quaver.pupil.sources.hitomi.Hitomi import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.util.Preferences -class DefaultQueryDialog(context : Context) : AlertDialog(context) { - - private val languages = context.resources.getStringArray(R.array.languages).map { - it.split("|").let { split -> - Pair(split[0], split[1]) - } - }.toMap() +class DefaultQueryDialogFragment() : DialogFragment() { + // TODO languageMap + private val languages = Hitomi.languageMap private val reverseLanguages = languages.entries.associate { (k, v) -> v to k } private val excludeBL = "-male:yaoi" @@ -45,40 +42,46 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) { var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null - private lateinit var binding: DefaultQueryDialogBinding + private var _binding: DefaultQueryDialogBinding? = null + private val binding get() = _binding!! - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setTitle(R.string.default_query_dialog_title) - binding = DefaultQueryDialogBinding.inflate(layoutInflater) - setView(binding.root) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + _binding = DefaultQueryDialogBinding.inflate(layoutInflater) initView() - setButton(Dialog.BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> - val newTags = Tags.parse(binding.edittext.text.toString()) + return AlertDialog.Builder(requireContext()).apply { + setTitle(R.string.default_query_dialog_title) + setView(binding.root) + setPositiveButton(android.R.string.ok) { _, _ -> + val newTags = Tags.parse(binding.edittext.text.toString()) - with(binding.languageSelector) { - if (selectedItemPosition != 0) - newTags.add("language:${reverseLanguages[selectedItem]}") + with(binding.languageSelector) { + if (selectedItemPosition != 0) + newTags.add("language:${reverseLanguages[selectedItem]}") + } + + if (binding.BLCheckbox.isChecked) + newTags.add(excludeBL) + + if (binding.guroCheckbox.isChecked) + excludeGuro.forEach { tag -> + newTags.add(tag) + } + + if (binding.loliCheckbox.isChecked) + excludeLoli.forEach { tag -> + newTags.add(tag) + } + + onPositiveButtonClickListener?.invoke(newTags) } + }.create() + } - if (binding.BLCheckbox.isChecked) - newTags.add(excludeBL) - - if (binding.guroCheckbox.isChecked) - excludeGuro.forEach { tag -> - newTags.add(tag) - } - - if (binding.loliCheckbox.isChecked) - excludeLoli.forEach { tag -> - newTags.add(tag) - } - - onPositiveButtonClickListener?.invoke(newTags) - } + override fun onDestroy() { + super.onDestroy() + _binding = null } private fun initView() { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt index fe935813..c4249c85 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt @@ -208,7 +208,7 @@ class GalleryDialog(context: Context, private val galleryID: String) : AlertDial val galleries = mutableListOf() val adapter = SearchResultsAdapter(galleries).apply { - onChipClickedHandler.add { tag -> + onChipClickedHandler = { tag -> this@GalleryDialog.onChipClickedHandler.forEach { handler -> handler.invoke(tag) } @@ -218,7 +218,7 @@ class GalleryDialog(context: Context, private val galleryID: String) : AlertDial GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { type.setText(R.string.gallery_related) - RecyclerView(context).apply { + contents.addView(RecyclerView(context).apply { layoutManager = LinearLayoutManager(context) this.adapter = adapter @@ -238,19 +238,12 @@ class GalleryDialog(context: Context, private val galleryID: String) : AlertDial true } } - } + }) CoroutineScope(Dispatchers.IO).launch { gallery.related.forEach { galleryID -> Cache.getInstance(context, galleryID.toString()).getGalleryBlock()?.let { - galleries.add( - Hitomi.SearchResult( - it.id.toString(), - it.title, - it.thumbnails.first(), - it.artists - ) - ) + galleries.add(Hitomi.transform(it)) } withContext(Dispatchers.Main) { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt index 29d4f625..d852a06c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt @@ -85,12 +85,12 @@ class SettingsFragment : DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog") } "default_query" -> { - DefaultQueryDialog(requireContext()).apply { + DefaultQueryDialogFragment().apply { onPositiveButtonClickListener = { newTags -> Preferences["default_query"] = newTags.toString() summary = newTags.toString() } - }.show() + }.show(parentFragmentManager, "Default Query Dialog") } "app_lock" -> { val intent = Intent(requireContext(), LockActivity::class.java).apply { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt index 917979da..6de28c78 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/view/TagChip.kt @@ -24,6 +24,7 @@ import androidx.core.content.ContextCompat import com.google.android.material.chip.Chip import xyz.quaver.pupil.R import xyz.quaver.pupil.favoriteTags +import xyz.quaver.pupil.sources.hitomi.Hitomi import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.util.translations import xyz.quaver.pupil.util.wordCapitalize @@ -39,12 +40,6 @@ class TagChip(context: Context, _tag: Tag) : Chip(context) { } } - private val languages = context.resources.getStringArray(R.array.languages).map { - it.split("|").let { split -> - Pair(split[0], split[1]) - } - }.toMap() - init { when(tag.area) { "male" -> { @@ -90,7 +85,8 @@ class TagChip(context: Context, _tag: Tag) : Chip(context) { } text = when (tag.area) { - "language" -> languages[tag.tag] + // TODO languageMap + "language" -> Hitomi.languageMap[tag.tag] else -> (translations[tag.tag] ?: tag.tag).wordCapitalize() } diff --git a/app/src/main/java/xyz/quaver/pupil/util/camera.kt b/app/src/main/java/xyz/quaver/pupil/util/camera.kt deleted file mode 100644 index 1b2e5652..00000000 --- a/app/src/main/java/xyz/quaver/pupil/util/camera.kt +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Pupil, Hitomi.la viewer for Android - * Copyright (C) 2020 tom5079 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -@file:Suppress("DEPRECATION", "Recycle") - -package xyz.quaver.pupil.util - -import android.content.Context -import android.content.pm.PackageManager -import android.graphics.ImageFormat -import android.graphics.SurfaceTexture -import android.hardware.Camera -import android.view.Surface -import android.view.WindowManager -import com.google.android.gms.tasks.Task -import com.google.mlkit.vision.common.InputImage -import com.google.mlkit.vision.face.Face -import com.google.mlkit.vision.face.FaceDetection -import com.google.mlkit.vision.face.FaceDetectorOptions - -/** Check if this device has a camera */ -private fun Context.checkCameraHardware() = - this.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) - -private fun openFrontCamera() : Pair { - var camera: Camera? = null - var cameraID: Int = -1 - - val cameraInfo = Camera.CameraInfo() - - for (i in 0 until Camera.getNumberOfCameras()) { - Camera.getCameraInfo(i, cameraInfo) - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) - runCatching { Camera.open(i) }.getOrNull()?.let { camera = it; cameraID = i } - - if (camera != null) break - } - - return Pair(camera, cameraID) -} - -val orientations = mapOf( - Surface.ROTATION_0 to 0, - Surface.ROTATION_90 to 90, - Surface.ROTATION_180 to 180, - Surface.ROTATION_270 to 270, -) - -private fun getRotation(context: Context, cameraID: Int): Int { - val cameraRotation = Camera.CameraInfo().also { Camera.getCameraInfo(cameraID, it) }.orientation - val rotation = orientations[(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation] ?: error("") - - return (cameraRotation + rotation) % 360 -} - -var camera: Camera? = null -var surfaceTexture: SurfaceTexture? = null -private val detector = FaceDetection.getClient( - FaceDetectorOptions.Builder() - .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL) - .build() -) -private var process: Task>? = null - -fun startCamera(context: Context, callback: (List) -> Unit) { - if (camera != null) closeCamera() - - val cameraID = openFrontCamera().let { (cam, cameraID) -> - cam ?: return - camera = cam - cameraID - } - - with (camera!!) { - parameters = parameters.apply { - setPreviewSize(640, 480) - previewFormat = ImageFormat.NV21 - } - - setPreviewTexture(surfaceTexture ?: SurfaceTexture(0).also { - surfaceTexture = it - }) - startPreview() - setPreviewCallback { bytes, _ -> - if (process?.isComplete == false) - return@setPreviewCallback - - val rotation = getRotation(context, cameraID) - - val image = InputImage.fromByteArray(bytes, 640, 480, rotation, InputImage.IMAGE_FORMAT_NV21) - process = detector.process(image) - .addOnSuccessListener(callback) - } - } -} - -fun closeCamera() { - camera?.setPreviewCallback(null) - camera?.stopPreview() - surfaceTexture?.release() - surfaceTexture = null - camera?.release() - camera = null -} \ No newline at end of file diff --git a/app/src/main/res/layout/progress_card_view.xml b/app/src/main/res/layout/progress_card_view.xml index 671a789f..ed004cd2 100644 --- a/app/src/main/res/layout/progress_card_view.xml +++ b/app/src/main/res/layout/progress_card_view.xml @@ -65,7 +65,7 @@ android:id="@+id/progressbar" android:layout_width="match_parent" android:layout_height="4dp" - android:progress="50" + android:visibility="gone" app:layout_constraintTop_toTopOf="parent"/> diff --git a/app/src/main/res/layout/reader_item.xml b/app/src/main/res/layout/reader_item.xml index 0cee0987..ea7c44fb 100644 --- a/app/src/main/res/layout/reader_item.xml +++ b/app/src/main/res/layout/reader_item.xml @@ -63,7 +63,7 @@ - @@ -70,30 +66,12 @@ app:layout_constraintTop_toBottomOf="@id/title" /> - - - - + app:layout_constraintLeft_toRightOf="@id/thumbnail"/> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index dfb2976b..85bb0c21 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -8,46 +8,6 @@ 50 - - indonesian|Bahasa Indonesia - catalan|català - cebuano|Cebuano - czech|Čeština - danish|Dansk - german|Deutsch - estonian|eesti - english|English - spanish|Español - esperanto|Esperanto - french|Français - italian|Italiano - latin|Latina - hungarian|magyar - dutch|Nederlands - norwegian|norsk - polish|polski - portuguese|Português - romanian|română - albanian|shqip - slovak|Slovenčina - finnish|Suomi - swedish|Svenska - tagalog|Tagalog - vietnamese|tiếng việt - turkish|Türkçe - greek|Ελληνικά - mongolian|Монгол - russian|Русский - ukrainian|Українська - hebrew|עברית - arabic|العربية - persian|فارسی - thai|ไทย - korean|한국어 - chinese|中文 - japanese|日本語 - - HITOMI|hitomi.la HIYOBI|hiyobi.me diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 90b50039..57b5cb5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,7 +100,7 @@ Series: %1$s Type: %1$s Language: %1$s - %dP + %sP diff --git a/gradle.properties b/gradle.properties index 2c2f1284..d53d94e5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,5 +19,6 @@ org.gradle.caching=true kotlin.code.style=official android.enableJetifier=true android.useAndroidX=true +android.enableBuildCache=true kotlin_version=1.4.20 \ No newline at end of file