Compare commits

..

16 Commits

Author SHA1 Message Date
tom5079
d0ad7effa0 Updated README.md 2021-02-13 18:14:18 +09:00
tom5079
a032beecbf Merge remote-tracking branch 'origin/master' 2021-02-13 18:13:52 +09:00
tom5079
46ec9e48d9 (Android 11) Show warning when the download folder is set to app internal space 2021-02-13 18:12:45 +09:00
tom5079
26bcef1cc0 Fixed Related gallery not showing up on GalleryDialog 2021-02-13 17:51:18 +09:00
tom5079
bfb2f44f8f Fixed favorite tag duplication 2021-02-13 17:43:44 +09:00
tom5079
28b19b6774 migration bug fixed 2021-01-13 09:49:49 +09:00
tom5079
8d72f4a3aa Update README.md 2021-01-12 12:55:50 +09:00
tom5079
9c62e0399d Update README.md 2021-01-12 12:43:09 +09:00
tom5079
65ea09854e Fixed Bug occuring on Android 11 2021-01-12 12:40:21 +09:00
tom5079
9f9a4c81b3 migrated to ViewBinding 2020-11-29 14:01:56 +09:00
tom5079
d567b30f4b Fixed typo & Built apk 2020-11-29 14:01:56 +09:00
tom5079
6d7c4ce0ab Fixed show extra tags button not showing up & version up 2020-11-29 14:01:51 +09:00
tom5079
e062b8f9e9 Implemented proper Page Turn without relying on RecyclerView 2020-11-29 14:01:46 +09:00
tom5079
08403b7a4e fixed gallery import 2020-11-29 14:01:41 +09:00
tom5079
c6ed5d35e7 ProgressCard 2020-11-29 14:01:25 +09:00
tom5079
dba3460b60 Fixes unable to recursively delete when the download folder is not not SAF based 2020-11-29 14:01:09 +09:00
48 changed files with 779 additions and 753 deletions

View File

@@ -2,7 +2,7 @@
*Pupil, Hitomi.la viewer for Android* *Pupil, Hitomi.la viewer for Android*
![](https://img.shields.io/github/downloads/tom5079/Pupil/total) ![](https://img.shields.io/github/downloads/tom5079/Pupil/total)
[![](https://img.shields.io/github/downloads/tom5079/Pupil/5.1.6-hotfix7/Pupil-v5.1.6-hotfix7.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.6-hotfix7/Pupil-v5.1.6-hotfix7.apk) [![](https://img.shields.io/github/downloads/tom5079/Pupil/5.1.7-hotfix1/Pupil-v5.1.7-hotfix1.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.7-hotfix1/Pupil-v5.1.7-hotfix1.apk)
[![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v) [![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v)
# Features # Features

View File

@@ -1,7 +1,7 @@
apply plugin: "com.android.application" apply plugin: "com.android.application"
apply plugin: "kotlin-android" apply plugin: "kotlin-android"
apply plugin: "kotlin-kapt" apply plugin: "kotlin-kapt"
apply plugin: "kotlin-android-extensions" apply plugin: "kotlin-parcelize"
apply plugin: "kotlinx-serialization" apply plugin: "kotlinx-serialization"
apply plugin: "com.google.android.gms.oss-licenses-plugin" apply plugin: "com.google.android.gms.oss-licenses-plugin"
@@ -38,7 +38,7 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 30 targetSdkVersion 30
versionCode 64 versionCode 64
versionName "5.1.7-beta1" versionName "5.1.7-hotfix1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
@@ -63,6 +63,9 @@ android {
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
} }
} }
buildFeatures {
viewBinding true
}
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString() jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental" freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental"
@@ -132,8 +135,4 @@ dependencies {
androidTestImplementation "androidx.test:rules:1.3.0" androidTestImplementation "androidx.test:rules:1.3.0"
androidTestImplementation "androidx.test:runner:1.3.0" androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0" androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
}
androidExtensions {
experimental = true
} }

View File

@@ -11,7 +11,7 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"versionCode": 64, "versionCode": 64,
"versionName": "5.1.7-beta1", "versionName": "5.1.7-hotfix1",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
] ]

View File

@@ -125,8 +125,13 @@ class Pupil : Application() {
favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse("")) favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "") searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
favoriteTags.filter { it.tag.contains('_') }.forEach {
favoriteTags.remove(it)
}
/*
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false) FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false)*/
try { try {
ProviderInstaller.installIfNeeded(this) ProviderInstaller.installIfNeeded(this)

View File

@@ -25,7 +25,6 @@ import android.graphics.drawable.Drawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -35,13 +34,12 @@ import com.daimajia.swipe.SwipeLayout
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.daimajia.swipe.interfaces.SwipeAdapterInterface
import com.github.piasy.biv.loader.ImageLoader import com.github.piasy.biv.loader.ImageLoader
import kotlinx.android.synthetic.main.item_galleryblock.view.*
import kotlinx.android.synthetic.main.view_progress_card.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import xyz.quaver.hitomi.getGallery import xyz.quaver.hitomi.getGallery
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.GalleryblockItemBinding
import xyz.quaver.pupil.favoriteTags import xyz.quaver.pupil.favoriteTags
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.Tag
@@ -57,20 +55,20 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
var updateAll = true var updateAll = true
var thin: Boolean = Preferences["thin"] var thin: Boolean = Preferences["thin"]
inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) { inner class GalleryViewHolder(val binding: GalleryblockItemBinding) : RecyclerView.ViewHolder(binding.root) {
private var galleryID: Int = 0 private var galleryID: Int = 0
init { init {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
while (updateAll) { while (updateAll) {
updateProgress(view.context) updateProgress(itemView.context)
delay(1000) delay(1000)
} }
} }
} }
private fun updateProgress(context: Context) = CoroutineScope(Dispatchers.Main).launch { private fun updateProgress(context: Context) = CoroutineScope(Dispatchers.Main).launch {
with(view.galleryblock_card) { with(binding.galleryblockCard) {
val imageList = Cache.getInstance(context, galleryID).metadata.imageList val imageList = Cache.getInstance(context, galleryID).metadata.imageList
if (imageList == null) { if (imageList == null) {
@@ -81,7 +79,7 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
progress = imageList.count { it != null } progress = imageList.count { it != null }
max = imageList.size max = imageList.size
view.galleryblock_id.setOnClickListener { this@GalleryViewHolder.binding.galleryblockId.setOnClickListener {
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip( (context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
ClipData.newPlainText("gallery_id", galleryID.toString()) ClipData.newPlainText("gallery_id", galleryID.toString())
) )
@@ -102,177 +100,175 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
fun bind(galleryID: Int) { fun bind(galleryID: Int) {
this.galleryID = galleryID this.galleryID = galleryID
updateProgress(view.context) updateProgress(itemView.context)
val cache = Cache.getInstance(view.context, galleryID) val cache = Cache.getInstance(itemView.context, galleryID)
val galleryBlock = runBlocking { val galleryBlock = runBlocking {
cache.getGalleryBlock() cache.getGalleryBlock()
} ?: return } ?: return
with(view) { val resources = itemView.context.resources
val resources = context.resources val languages = resources.getStringArray(R.array.languages).map {
val languages = resources.getStringArray(R.array.languages).map { it.split("|").let { split ->
it.split("|").let { split -> Pair(split[0], split[1])
Pair(split[0], split[1])
}
}.toMap()
val artists = galleryBlock.artists
val series = galleryBlock.series
galleryblock_thumbnail.apply {
setOnClickListener {
view.performClick()
}
setOnLongClickListener {
view.performLongClick()
}
setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant))
setImageLoaderCallback(object: ImageLoader.Callback {
override fun onFail(error: Exception?) {
Cache.getInstance(context, galleryID).let { cache ->
cache.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() }
cache.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() }
}
}
override fun onCacheHit(imageType: Int, image: File?) {}
override fun onCacheMiss(imageType: Int, image: File?) {}
override fun onFinish() {}
override fun onProgress(progress: Int) {}
override fun onStart() {}
override fun onSuccess(image: File?) {}
})
ssiv?.recycle()
CoroutineScope(Dispatchers.IO).launch {
cache.getThumbnail().let { launch(Dispatchers.Main) {
showImage(it)
} }
}
} }
}.toMap()
galleryblock_title.text = galleryBlock.title val artists = galleryBlock.artists
with(galleryblock_artist) { val series = galleryBlock.series
text = artists.joinToString { it.wordCapitalize() }
visibility = when {
artists.isNotEmpty() -> View.VISIBLE
else -> View.GONE
}
CoroutineScope(Dispatchers.IO).launch { binding.galleryblockThumbnail.apply {
val gallery = runCatching { setOnClickListener {
getGallery(galleryID) itemView.performClick()
}.getOrNull()
if (gallery?.groups?.isNotEmpty() != true)
return@launch
launch(Dispatchers.Main) {
text = context.getString(
R.string.galleryblock_artist_with_group,
artists.joinToString { it.wordCapitalize() },
gallery.groups.joinToString { it.wordCapitalize() }
)
}
}
} }
with(galleryblock_series) { setOnLongClickListener {
text = itemView.performLongClick()
resources.getString(
R.string.galleryblock_series,
series.joinToString(", ") { it.wordCapitalize() })
visibility = when {
series.isNotEmpty() -> View.VISIBLE
else -> View.GONE
}
} }
galleryblock_type.text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize() setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant))
with(galleryblock_language) { setImageLoaderCallback(object: ImageLoader.Callback {
text = override fun onFail(error: Exception?) {
resources.getString(R.string.galleryblock_language, languages[galleryBlock.language]) Cache.getInstance(context, galleryID).let { cache ->
visibility = when { cache.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() }
galleryBlock.language.isNotEmpty() -> View.VISIBLE cache.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() }
else -> View.GONE
}
}
with(galleryblock_tag_group) {
onClickListener = {
onChipClickedHandler.forEach { callback ->
callback.invoke(it)
} }
} }
tags.clear() override fun onCacheHit(imageType: Int, image: File?) {}
override fun onCacheMiss(imageType: Int, image: File?) {}
CoroutineScope(Dispatchers.IO).launch { override fun onFinish() {}
tags.addAll( override fun onProgress(progress: Int) {}
galleryBlock.relatedTags.sortedBy { override fun onStart() {}
val tag = Tag.parse(it) override fun onSuccess(image: File?) {}
})
if (favoriteTags.contains(tag)) ssiv?.recycle()
-1
else
when(Tag.parse(it).area) {
"female" -> 0
"male" -> 1
else -> 2
}
}.map {
Tag.parse(it)
}
)
launch(Dispatchers.Main) {
refresh()
}
}
}
galleryblock_id.text = galleryBlock.id.toString()
galleryblock_pagecount.text = "-"
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val pageCount = kotlin.runCatching { cache.getThumbnail().let { launch(Dispatchers.Main) {
getReader(galleryBlock.id).galleryInfo.files.size showImage(it)
}.getOrNull() ?: return@launch } }
withContext(Dispatchers.Main) { }
galleryblock_pagecount.text = context.getString(R.string.galleryblock_pagecount, pageCount) }
binding.galleryblockTitle.text = galleryBlock.title
with(binding.galleryblockArtist) {
text = artists.joinToString { it.wordCapitalize() }
visibility = when {
artists.isNotEmpty() -> View.VISIBLE
else -> View.GONE
}
CoroutineScope(Dispatchers.IO).launch {
val gallery = runCatching {
getGallery(galleryID)
}.getOrNull()
if (gallery?.groups?.isNotEmpty() != true)
return@launch
launch(Dispatchers.Main) {
text = context.getString(
R.string.galleryblock_artist_with_group,
artists.joinToString { it.wordCapitalize() },
gallery.groups.joinToString { it.wordCapitalize() }
)
}
}
}
with(binding.galleryblockSeries) {
text =
resources.getString(
R.string.galleryblock_series,
series.joinToString(", ") { it.wordCapitalize() })
visibility = when {
series.isNotEmpty() -> View.VISIBLE
else -> View.GONE
}
}
binding.galleryblockType.text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize()
with(binding.galleryblockLanguage) {
text =
resources.getString(R.string.galleryblock_language, languages[galleryBlock.language])
visibility = when {
galleryBlock.language.isNotEmpty() -> View.VISIBLE
else -> View.GONE
}
}
with(binding.galleryblockTagGroup) {
onClickListener = {
onChipClickedHandler.forEach { callback ->
callback.invoke(it)
} }
} }
with(galleryblock_favorite) { tags.clear()
setImageResource(if (favorites.contains(galleryBlock.id)) R.drawable.ic_star_filled else R.drawable.ic_star_empty)
setOnClickListener {
when {
favorites.contains(galleryBlock.id) -> {
favorites.remove(galleryBlock.id)
setImageResource(R.drawable.ic_star_empty) CoroutineScope(Dispatchers.IO).launch {
} tags.addAll(
else -> { galleryBlock.relatedTags.sortedBy {
favorites.add(galleryBlock.id) val tag = Tag.parse(it)
setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star).apply { if (favoriteTags.contains(tag))
this ?: return@apply -1
else
when(Tag.parse(it).area) {
"female" -> 0
"male" -> 1
else -> 2
}
}.map {
Tag.parse(it)
}
)
registerAnimationCallback(object: Animatable2Compat.AnimationCallback() { launch(Dispatchers.Main) {
override fun onAnimationEnd(drawable: Drawable?) { refresh()
setImageResource(R.drawable.ic_star_filled) }
} }
}) }
start()
binding.galleryblockId.text = galleryBlock.id.toString()
binding.galleryblockPagecount.text = "-"
CoroutineScope(Dispatchers.IO).launch {
val pageCount = kotlin.runCatching {
getReader(galleryBlock.id).galleryInfo.files.size
}.getOrNull() ?: return@launch
withContext(Dispatchers.Main) {
binding.galleryblockPagecount.text = itemView.context.getString(R.string.galleryblock_pagecount, pageCount)
}
}
with(binding.galleryblockFavorite) {
setImageResource(if (favorites.contains(galleryBlock.id)) R.drawable.ic_star_filled else R.drawable.ic_star_empty)
setOnClickListener {
when {
favorites.contains(galleryBlock.id) -> {
favorites.remove(galleryBlock.id)
setImageResource(R.drawable.ic_star_empty)
}
else -> {
favorites.add(galleryBlock.id)
setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star).apply {
this ?: return@apply
registerAnimationCallback(object: Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
setImageResource(R.drawable.ic_star_filled)
}
}) })
} start()
})
} }
} }
} }
}
// Make some views invisible to make it thinner // Make some views invisible to make it thinner
if (thin) { if (thin) {
galleryblock_tag_group.visibility = View.GONE binding.galleryblockTagGroup.visibility = View.GONE
}
} }
} }
} }
@@ -282,9 +278,7 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
var onDeleteClickedHandler: ((Int) -> Unit)? = null var onDeleteClickedHandler: ((Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return GalleryViewHolder( return GalleryViewHolder(GalleryblockItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
LayoutInflater.from(parent.context).inflate(R.layout.item_galleryblock, parent, false)
)
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
@@ -293,25 +287,25 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
holder.bind(galleryID) holder.bind(galleryID)
holder.view.galleryblock_card.download.setOnClickListener { holder.binding.galleryblockCard.binding.download.setOnClickListener {
onDownloadClickedHandler?.invoke(position) onDownloadClickedHandler?.invoke(position)
} }
holder.view.galleryblock_card.delete.setOnClickListener { holder.binding.galleryblockCard.binding.delete.setOnClickListener {
onDeleteClickedHandler?.invoke(position) onDeleteClickedHandler?.invoke(position)
} }
mItemManger.bindView(holder.view, position) mItemManger.bindView(holder.binding.root, position)
holder.view.galleryblock_card.swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener { holder.binding.galleryblockCard.binding.swipeLayout.addSwipeListener(object: SwipeLayout.SwipeListener {
override fun onStartOpen(layout: SwipeLayout?) { override fun onStartOpen(layout: SwipeLayout?) {
mItemManger.closeAllExcept(layout) mItemManger.closeAllExcept(layout)
holder.view.galleryblock_card.download.text = holder.binding.galleryblockCard.binding.download.text =
if (DownloadManager.getInstance(holder.view.context).isDownloading(galleryID)) if (DownloadManager.getInstance(holder.binding.root.context).isDownloading(galleryID))
holder.view.context.getString(android.R.string.cancel) holder.binding.root.context.getString(android.R.string.cancel)
else else
holder.view.context.getString(R.string.main_download) holder.binding.root.context.getString(R.string.main_download)
} }
override fun onClose(layout: SwipeLayout?) {} override fun onClose(layout: SwipeLayout?) {}

View File

@@ -22,17 +22,29 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_mirrors.view.*
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.MirrorsItemBinding
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
import java.util.* import java.util.*
class MirrorAdapter(context: Context) : RecyclerView.Adapter<MirrorAdapter.ViewHolder>() { class MirrorAdapter(context: Context) : RecyclerView.Adapter<MirrorAdapter.ViewHolder>() {
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) @SuppressLint("ClickableViewAccessibility")
inner class ViewHolder(val binding: MirrorsItemBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.mirrorButton.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN)
onStartDrag?.invoke(this)
true
}
}
fun bind(mirror: String) {
binding.mirrorName.text = mirror
}
}
val mirrors = context.resources.getStringArray(R.array.mirrors).map { val mirrors = context.resources.getStringArray(R.array.mirrors).map {
it.split('|').let { split -> it.split('|').let { split ->
@@ -62,23 +74,11 @@ class MirrorAdapter(context: Context) : RecyclerView.Adapter<MirrorAdapter.ViewH
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder.view) { holder.bind(mirrors[list.elementAt(position)] ?: error(""))
mirror_name.text = mirrors[list.elementAt(position)]
mirror_button.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN)
onStartDrag?.invoke(holder)
true
}
}
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return LayoutInflater.from(parent.context).inflate( return ViewHolder(MirrorsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
R.layout.item_mirrors, parent, false
).let {
ViewHolder(it)
}
} }
override fun getItemCount() = mirrors.size override fun getItemCount() = mirrors.size

View File

@@ -39,10 +39,10 @@ import com.facebook.imagepipeline.image.ImageInfo
import com.github.piasy.biv.view.BigImageView import com.github.piasy.biv.view.BigImageView
import com.github.piasy.biv.view.ImageShownCallback import com.github.piasy.biv.view.ImageShownCallback
import com.github.piasy.biv.view.ImageViewFactory import com.github.piasy.biv.view.ImageViewFactory
import kotlinx.android.synthetic.main.item_reader.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.ReaderItemBinding
import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.ui.ReaderActivity
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import java.io.File import java.io.File
@@ -52,16 +52,93 @@ class ReaderAdapter(
private val activity: ReaderActivity, private val activity: ReaderActivity,
private val galleryID: Int private val galleryID: Int
) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() { ) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
var reader: Reader? = null var reader: Reader? = null
var isFullScreen = false var isFullScreen = false
var onItemClickListener : (() -> (Unit))? = null var onItemClickListener : (() -> (Unit))? = null
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { inner class ViewHolder(private val binding: ReaderItemBinding) : RecyclerView.ViewHolder(binding.root) {
init {
with (binding.image) {
setImageViewFactory(FrescoImageViewFactory().apply {
updateView = { imageInfo ->
binding.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
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()
}
}
binding.root.setOnClickListener {
onItemClickListener?.invoke()
}
}
fun bind(position: Int) {
if (cache == null)
cache = Cache.getInstance(itemView.context, galleryID)
if (!isFullScreen) {
binding.root.setBackgroundResource(R.drawable.reader_item_boundary)
binding.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
height = 0
dimensionRatio =
"${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
}
} else {
binding.root.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
height = ConstraintLayout.LayoutParams.MATCH_PARENT
dimensionRatio = null
}
binding.root.background = null
}
binding.readerIndex.text = (position+1).toString()
val image = cache!!.getImage(position)
val progress = activity.downloader?.progress?.get(galleryID)?.get(position)
if (progress?.isInfinite() == true && image != null) {
binding.progressGroup.visibility = View.INVISIBLE
binding.image.showImage(image.uri)
} else {
binding.progressGroup.visibility = View.VISIBLE
binding.readerItemProgressbar.progress =
if (progress?.isInfinite() == true)
100
else
progress?.roundToInt() ?: 0
clear()
CoroutineScope(Dispatchers.Main).launch {
delay(1000)
notifyItemChanged(position)
}
}
}
fun clear() { fun clear() {
view.image.mainView.let { binding.image.mainView.let {
when (it) { when (it) {
is SubsamplingScaleImageView -> is SubsamplingScaleImageView ->
it.recycle() it.recycle()
@@ -73,88 +150,12 @@ class ReaderAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return LayoutInflater.from(parent.context).inflate( return ViewHolder(ReaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
R.layout.item_reader, parent, false
).let {
with(it) {
image.setImageViewFactory(FrescoImageViewFactory().apply {
updateView = { imageInfo ->
it.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
dimensionRatio = "${imageInfo.width}:${imageInfo.height}"
}
}
})
image.setImageShownCallback(object : ImageShownCallback {
override fun onMainImageShown() {
it.image.mainView.let { v ->
when (v) {
is SubsamplingScaleImageView ->
if (!isFullScreen) it.image.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
}
}
override fun onThumbnailShown() {}
})
image.setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant))
image.setOnClickListener {
this.performClick()
}
setOnClickListener {
onItemClickListener?.invoke()
}
}
ViewHolder(it)
}
} }
private var cache: Cache? = null private var cache: Cache? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.view as ConstraintLayout holder.bind(position)
if (cache == null)
cache = Cache.getInstance(holder.view.context, galleryID)
if (!isFullScreen) {
holder.view.setBackgroundResource(R.drawable.reader_item_boundary)
holder.view.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
height = 0
dimensionRatio =
"${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
}
} else {
holder.view.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
holder.view.image.updateLayoutParams<ConstraintLayout.LayoutParams> {
height = ConstraintLayout.LayoutParams.MATCH_PARENT
dimensionRatio = null
}
holder.view.background = null
}
holder.view.reader_index.text = (position+1).toString()
val image = cache!!.getImage(position)
val progress = activity.downloader?.progress?.get(galleryID)?.get(position)
if (progress?.isInfinite() == true && image != null) {
holder.view.progress_group.visibility = View.INVISIBLE
holder.view.image.showImage(image.uri)
} else {
holder.view.progress_group.visibility = View.VISIBLE
holder.view.reader_item_progressbar.progress =
if (progress?.isInfinite() == true)
100
else
progress?.roundToInt() ?: 0
holder.clear()
CoroutineScope(Dispatchers.Main).launch {
delay(1000)
notifyItemChanged(position)
}
}
} }
override fun getItemCount() = reader?.galleryInfo?.files?.size ?: 0 override fun getItemCount() = reader?.galleryInfo?.files?.size ?: 0

View File

@@ -18,8 +18,8 @@
package xyz.quaver.pupil.types package xyz.quaver.pupil.types
import kotlinx.android.parcel.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
import xyz.quaver.hitomi.Suggestion import xyz.quaver.hitomi.Suggestion
import xyz.quaver.pupil.util.translations import xyz.quaver.pupil.util.translations

View File

@@ -29,10 +29,8 @@ import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.andrognito.patternlockview.PatternLockView import com.andrognito.patternlockview.PatternLockView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_lock.*
import kotlinx.android.synthetic.main.fragment_pattern_lock.*
import kotlinx.android.synthetic.main.fragment_pin_lock.*
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.LockActivityBinding
import xyz.quaver.pupil.ui.fragment.PINLockFragment import xyz.quaver.pupil.ui.fragment.PINLockFragment
import xyz.quaver.pupil.ui.fragment.PatternLockFragment import xyz.quaver.pupil.ui.fragment.PatternLockFragment
import xyz.quaver.pupil.util.Lock import xyz.quaver.pupil.util.Lock
@@ -45,6 +43,8 @@ class LockActivity : AppCompatActivity() {
private lateinit var lockManager: LockManager private lateinit var lockManager: LockManager
private var mode: String? = null private var mode: String? = null
private lateinit var binding: LockActivityBinding
private val patternLockFragment = PatternLockFragment().apply { private val patternLockFragment = PatternLockFragment().apply {
var lastPass = "" var lastPass = ""
onPatternDrawn = { onPatternDrawn = {
@@ -57,7 +57,7 @@ class LockActivity : AppCompatActivity() {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} else } else
lock_pattern_view.setViewMode(PatternLockView.PatternViewMode.WRONG) binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
} }
"add_lock" -> { "add_lock" -> {
if (lastPass.isEmpty()) { if (lastPass.isEmpty()) {
@@ -69,7 +69,7 @@ class LockActivity : AppCompatActivity() {
LockManager(context!!).add(Lock.generate(Lock.Type.PATTERN, it)) LockManager(context!!).add(Lock.generate(Lock.Type.PATTERN, it))
finish() finish()
} else { } else {
lock_pattern_view.setViewMode(PatternLockView.PatternViewMode.WRONG) binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
lastPass = "" lastPass = ""
Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show() Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
@@ -92,15 +92,15 @@ class LockActivity : AppCompatActivity() {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} else { } else {
indicator_dots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply { binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
setAnimationListener(object: Animation.AnimationListener { setAnimationListener(object: Animation.AnimationListener {
override fun onAnimationEnd(animation: Animation?) { override fun onAnimationEnd(animation: Animation?) {
pin_lock_view.resetPinLockView() binding.pinLockView.resetPinLockView()
pin_lock_view.isEnabled = true binding.pinLockView.isEnabled = true
} }
override fun onAnimationStart(animation: Animation?) { override fun onAnimationStart(animation: Animation?) {
pin_lock_view.isEnabled = false binding.pinLockView.isEnabled = false
} }
override fun onAnimationRepeat(animation: Animation?) { override fun onAnimationRepeat(animation: Animation?) {
@@ -114,22 +114,22 @@ class LockActivity : AppCompatActivity() {
if (lastPass.isEmpty()) { if (lastPass.isEmpty()) {
lastPass = it lastPass = it
pin_lock_view.resetPinLockView() binding.pinLockView.resetPinLockView()
Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show() Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show()
} else { } else {
if (lastPass == it) { if (lastPass == it) {
LockManager(context!!).add(Lock.generate(Lock.Type.PIN, it)) LockManager(context!!).add(Lock.generate(Lock.Type.PIN, it))
finish() finish()
} else { } else {
indicator_dots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply { binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
setAnimationListener(object: Animation.AnimationListener { setAnimationListener(object: Animation.AnimationListener {
override fun onAnimationEnd(animation: Animation?) { override fun onAnimationEnd(animation: Animation?) {
pin_lock_view.resetPinLockView() binding.pinLockView.resetPinLockView()
pin_lock_view.isEnabled = true binding.pinLockView.isEnabled = true
} }
override fun onAnimationStart(animation: Animation?) { override fun onAnimationStart(animation: Animation?) {
pin_lock_view.isEnabled = false binding.pinLockView.isEnabled = false
} }
override fun onAnimationRepeat(animation: Animation?) { override fun onAnimationRepeat(animation: Animation?) {
@@ -173,7 +173,8 @@ class LockActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lock) binding = LockActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
lockManager = try { lockManager = try {
LockManager(this) LockManager(this)
@@ -210,7 +211,7 @@ class LockActivity : AppCompatActivity() {
Preferences["lock_fingerprint"] Preferences["lock_fingerprint"]
&& BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS && BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
) { ) {
lock_fingerprint.apply { binding.fingerprintBtn.apply {
isEnabled = true isEnabled = true
setOnClickListener { setOnClickListener {
showBiometricPrompt() showBiometricPrompt()
@@ -219,7 +220,7 @@ class LockActivity : AppCompatActivity() {
showBiometricPrompt() showBiometricPrompt()
} }
lock_pattern.apply { binding.patternBtn.apply {
isEnabled = lockManager.contains(Lock.Type.PATTERN) isEnabled = lockManager.contains(Lock.Type.PATTERN)
setOnClickListener { setOnClickListener {
supportFragmentManager.beginTransaction().replace( supportFragmentManager.beginTransaction().replace(
@@ -227,7 +228,7 @@ class LockActivity : AppCompatActivity() {
).commit() ).commit()
} }
} }
lock_pin.apply { binding.pinBtn.apply {
isEnabled = lockManager.contains(Lock.Type.PIN) isEnabled = lockManager.contains(Lock.Type.PIN)
setOnClickListener { setOnClickListener {
supportFragmentManager.beginTransaction().replace( supportFragmentManager.beginTransaction().replace(
@@ -235,7 +236,7 @@ class LockActivity : AppCompatActivity() {
).commit() ).commit()
} }
} }
lock_password.isEnabled = false binding.passwordBtn.isEnabled = false
when (lockManager.locks!!.first().type) { when (lockManager.locks!!.first().type) {
Lock.Type.PIN -> { Lock.Type.PIN -> {
@@ -253,20 +254,20 @@ class LockActivity : AppCompatActivity() {
} }
} }
"add_lock" -> { "add_lock" -> {
lock_pattern.isEnabled = false binding.patternBtn.isEnabled = false
lock_pin.isEnabled = false binding.pinBtn.isEnabled = false
lock_fingerprint.isEnabled = false binding.fingerprintBtn.isEnabled = false
lock_password.isEnabled = false binding.passwordBtn.isEnabled = false
when(intent.getStringExtra("type")!!) { when(intent.getStringExtra("type")!!) {
"pattern" -> { "pattern" -> {
lock_pattern.isEnabled = true binding.patternBtn.isEnabled = true
supportFragmentManager.beginTransaction().add( supportFragmentManager.beginTransaction().add(
R.id.lock_content, patternLockFragment R.id.lock_content, patternLockFragment
).commit() ).commit()
} }
"pin" -> { "pin" -> {
lock_pin.isEnabled = true binding.pinBtn.isEnabled = true
supportFragmentManager.beginTransaction().add( supportFragmentManager.beginTransaction().add(
R.id.lock_content, pinLockFragment R.id.lock_content, pinLockFragment
).commit() ).commit()

View File

@@ -21,6 +21,7 @@ package xyz.quaver.pupil.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.text.util.Linkify import android.text.util.Linkify
@@ -31,16 +32,14 @@ import android.view.animation.DecelerateInterpolator
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main_content.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import xyz.quaver.floatingsearchview.FloatingSearchView import xyz.quaver.floatingsearchview.FloatingSearchView
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
@@ -51,6 +50,7 @@ import xyz.quaver.hitomi.getGalleryIDsFromNozomi
import xyz.quaver.hitomi.getSuggestionsForQuery import xyz.quaver.hitomi.getSuggestionsForQuery
import xyz.quaver.pupil.* import xyz.quaver.pupil.*
import xyz.quaver.pupil.adapters.GalleryBlockAdapter import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.databinding.MainActivityBinding
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.types.* import xyz.quaver.pupil.types.*
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment
@@ -103,18 +103,20 @@ class MainActivity :
private var loadingJob: Job? = null private var loadingJob: Job? = null
private var currentPage = 0 private var currentPage = 0
private lateinit var binding: MainActivityBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) binding = MainActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
if (intent.action == Intent.ACTION_VIEW) { if (intent.action == Intent.ACTION_VIEW) {
intent.dataString?.let { url -> intent.dataString?.let { url ->
restore(url, restore(url,
onFailure = { onFailure = {
Snackbar.make(this.main_recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show() Snackbar.make(binding.contents.recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
}, onSuccess = { }, onSuccess = {
Snackbar.make(this.main_recyclerview, getString(R.string.settings_restore_success, it.size), Snackbar.LENGTH_LONG).show() Snackbar.make(binding.contents.recyclerview, getString(R.string.settings_restore_success, it.size), Snackbar.LENGTH_LONG).show()
} }
) )
} }
@@ -125,13 +127,28 @@ class MainActivity :
checkUpdate(this) checkUpdate(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Preferences["download_folder_ignore_warning", false] &&
ContextCompat.getExternalFilesDirs(this, null).map { Uri.fromFile(it).toString() }
.contains(Preferences["download_folder", ""])
) {
AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(R.string.unaccessible_download_folder)
.setPositiveButton(android.R.string.ok) { _, _ ->
DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog")
}.setNegativeButton(R.string.ignore) { _, _ ->
Preferences["download_folder_ignore_warning"] = true
}.show()
}
initView() initView()
} }
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
override fun onBackPressed() { override fun onBackPressed() {
when { when {
main_drawer_layout.isDrawerOpen(GravityCompat.START) -> main_drawer_layout.closeDrawer(GravityCompat.START) binding.drawer.isDrawerOpen(GravityCompat.START) -> binding.drawer.closeDrawer(GravityCompat.START)
queryStack.removeLastOrNull() != null && queryStack.isNotEmpty() -> runOnUiThread { queryStack.removeLastOrNull() != null && queryStack.isNotEmpty() -> runOnUiThread {
query = queryStack.last() query = queryStack.last()
@@ -147,7 +164,7 @@ class MainActivity :
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
(main_recyclerview?.adapter as? GalleryBlockAdapter)?.updateAll = false (binding.contents.recyclerview.adapter as? GalleryBlockAdapter)?.updateAll = false
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@@ -188,36 +205,36 @@ class MainActivity :
} }
private fun initView() { private fun initView() {
main_recyclerview.addOnScrollListener(object: RecyclerView.OnScrollListener() { binding.contents.recyclerview.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
// -height of the search view < translationY < 0 // -height of the search view < translationY < 0
main_searchview.translationY = binding.contents.searchview.translationY =
min( min(
max( max(
main_searchview.translationY - dy, binding.contents.searchview.translationY - dy,
-main_searchview.findViewById<CardView>(R.id.search_query_section).height.toFloat() -binding.contents.searchview.findViewById<CardView>(R.id.search_query_section).height.toFloat()
), 0F) ), 0F)
if (dy > 0) if (dy > 0)
main_fab.hideMenuButton(true) binding.contents.fab.hideMenuButton(true)
else if (dy < 0) else if (dy < 0)
main_fab.showMenuButton(true) binding.contents.fab.showMenuButton(true)
} }
}) })
Linkify.addLinks(main_noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) }) Linkify.addLinks(binding.contents.noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) })
//NavigationView //NavigationView
main_nav_view.setNavigationItemSelectedListener(this) binding.navView.setNavigationItemSelectedListener(this)
with(main_fab_cancel) { with(binding.contents.cancelFab) {
setImageResource(R.drawable.cancel) setImageResource(R.drawable.cancel)
setOnClickListener { setOnClickListener {
DownloadService.cancel(this@MainActivity) DownloadService.cancel(this@MainActivity)
} }
} }
with(main_fab_jump) { with(binding.contents.jumpFab) {
setImageResource(R.drawable.ic_jump) setImageResource(R.drawable.ic_jump)
setOnClickListener { setOnClickListener {
val perPage = Preferences["per_page", "25"].toInt() val perPage = Preferences["per_page", "25"].toInt()
@@ -245,7 +262,7 @@ class MainActivity :
} }
} }
with(main_fab_random) { with(binding.contents.randomFab) {
setImageResource(R.drawable.shuffle_variant) setImageResource(R.drawable.shuffle_variant)
setOnClickListener { setOnClickListener {
runBlocking { runBlocking {
@@ -275,7 +292,7 @@ class MainActivity :
} }
} }
with(main_fab_id) { with(binding.contents.idFab) {
setImageResource(R.drawable.numeric) setImageResource(R.drawable.numeric)
setOnClickListener { setOnClickListener {
val editText = EditText(context).apply { val editText = EditText(context).apply {
@@ -308,7 +325,7 @@ class MainActivity :
} }
} }
with(main_view) { with(binding.contents.view) {
setOnPageTurnListener(object: MainView.OnPageTurnListener { setOnPageTurnListener(object: MainView.OnPageTurnListener {
override fun onPrev(page: Int) { override fun onPrev(page: Int) {
currentPage-- currentPage--
@@ -316,7 +333,7 @@ class MainActivity :
// disable pageturn until the contents are loaded // disable pageturn until the contents are loaded
setCurrentPage(1, false) setCurrentPage(1, false)
ViewCompat.animate(main_searchview) ViewCompat.animate(binding.contents.searchview)
.setDuration(100) .setDuration(100)
.setInterpolator(DecelerateInterpolator()) .setInterpolator(DecelerateInterpolator())
.translationY(0F) .translationY(0F)
@@ -333,7 +350,7 @@ class MainActivity :
// disable pageturn until the contents are loaded // disable pageturn until the contents are loaded
setCurrentPage(1, false) setCurrentPage(1, false)
ViewCompat.animate(main_searchview) ViewCompat.animate(binding.contents.searchview)
.setDuration(100) .setDuration(100)
.setInterpolator(DecelerateInterpolator()) .setInterpolator(DecelerateInterpolator())
.translationY(0F) .translationY(0F)
@@ -354,7 +371,7 @@ class MainActivity :
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun setupRecyclerView() { private fun setupRecyclerView() {
with(main_recyclerview) { with(binding.contents.recyclerview) {
adapter = GalleryBlockAdapter(galleries).apply { adapter = GalleryBlockAdapter(galleries).apply {
onChipClickedHandler.add { onChipClickedHandler.add {
runOnUiThread { runOnUiThread {
@@ -454,10 +471,10 @@ class MainActivity :
private var suggestionJob : Job? = null private var suggestionJob : Job? = null
private fun setupSearchBar() { private fun setupSearchBar() {
with(main_searchview as xyz.quaver.pupil.ui.view.FloatingSearchView) { with(binding.contents.searchview) {
onMenuStatusChangeListener = object: FloatingSearchView.OnMenuStatusChangeListener { onMenuStatusChangeListener = object: FloatingSearchView.OnMenuStatusChangeListener {
override fun onMenuOpened() { override fun onMenuOpened() {
(this@MainActivity.main_recyclerview.adapter as GalleryBlockAdapter).closeAllItems() (binding.contents.recyclerview.adapter as GalleryBlockAdapter).closeAllItems()
} }
override fun onMenuClosed() { override fun onMenuClosed() {
@@ -541,7 +558,7 @@ class MainActivity :
} }
} }
attachNavigationDrawerToMenuButton(main_drawer_layout) attachNavigationDrawerToMenuButton(binding.drawer)
} }
} }
@@ -552,7 +569,7 @@ class MainActivity :
val thin = !item.isChecked val thin = !item.isChecked
item.isChecked = thin item.isChecked = thin
main_recyclerview.apply { binding.contents.recyclerview.apply {
(adapter as GalleryBlockAdapter).apply { (adapter as GalleryBlockAdapter).apply {
this.thin = thin this.thin = thin
@@ -593,7 +610,7 @@ class MainActivity :
override fun onNavigationItemSelected(item: MenuItem): Boolean { override fun onNavigationItemSelected(item: MenuItem): Boolean {
runOnUiThread { runOnUiThread {
main_drawer_layout.closeDrawers() binding.drawer.closeDrawers()
when(item.itemId) { when(item.itemId) {
R.id.main_drawer_home -> { R.id.main_drawer_home -> {
@@ -665,14 +682,14 @@ class MainActivity :
private fun clearGalleries() = CoroutineScope(Dispatchers.Main).launch { private fun clearGalleries() = CoroutineScope(Dispatchers.Main).launch {
galleries.clear() galleries.clear()
with(main_recyclerview.adapter as GalleryBlockAdapter?) { with(binding.contents.recyclerview.adapter as GalleryBlockAdapter?) {
this ?: return@with this ?: return@with
this.notifyDataSetChanged() this.notifyDataSetChanged()
} }
main_noresult.visibility = View.INVISIBLE binding.contents.noresult.visibility = View.INVISIBLE
main_progressbar.show() binding.contents.progressbar.show()
} }
private fun fetchGalleries(query: String, sortMode: SortMode) { private fun fetchGalleries(query: String, sortMode: SortMode) {
@@ -687,7 +704,7 @@ class MainActivity :
} }
if (query.isNotEmpty() && mode != Mode.SEARCH) { if (query.isNotEmpty() && mode != Mode.SEARCH) {
Snackbar.make(this@MainActivity.main_recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply { Snackbar.make(binding.contents.recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply {
setAction(android.R.string.ok) { setAction(android.R.string.ok) {
cancelFetch() cancelFetch()
clearGalleries() clearGalleries()
@@ -784,15 +801,15 @@ class MainActivity :
FirebaseCrashlytics.getInstance().recordException(e) FirebaseCrashlytics.getInstance().recordException(e)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
main_noresult.visibility = View.VISIBLE binding.contents.noresult.visibility = View.VISIBLE
main_progressbar.hide() binding.contents.progressbar.hide()
} }
return@launch return@launch
} }
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
main_view.setCurrentPage(currentPage + 1, galleryIDs.size > (currentPage+1)*perPage) binding.contents.view.setCurrentPage(currentPage + 1, galleryIDs.size > (currentPage+1)*perPage)
} }
galleryIDs.slice(currentPage*perPage until min(currentPage*perPage+perPage, galleryIDs.size)).chunked(5).let { chunks -> galleryIDs.slice(currentPage*perPage until min(currentPage*perPage+perPage, galleryIDs.size)).chunked(5).let { chunks ->
@@ -806,10 +823,10 @@ class MainActivity :
}.forEach { }.forEach {
it.await()?.also { it.await()?.also {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
main_progressbar.hide() binding.contents.progressbar.hide()
galleries.add(it) galleries.add(it)
main_recyclerview.adapter!!.notifyItemInserted(galleries.size - 1) binding.contents.recyclerview.adapter!!.notifyItemInserted(galleries.size - 1)
} }
} }
} }

View File

@@ -45,10 +45,6 @@ import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.mlkit.vision.face.Face import com.google.mlkit.vision.face.Face
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
import kotlinx.android.synthetic.main.activity_reader.*
import kotlinx.android.synthetic.main.activity_reader.view.*
import kotlinx.android.synthetic.main.dialog_numberpicker.view.*
import kotlinx.android.synthetic.main.reader_eye_card.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -56,6 +52,8 @@ import kotlinx.coroutines.launch
import xyz.quaver.Code import xyz.quaver.Code
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.ReaderAdapter import xyz.quaver.pupil.adapters.ReaderAdapter
import xyz.quaver.pupil.databinding.NumberpickerDialogBinding
import xyz.quaver.pupil.databinding.ReaderActivityBinding
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
@@ -75,7 +73,7 @@ class ReaderActivity : BaseActivity() {
set(value) { set(value) {
field = value field = value
(reader_recyclerview.adapter as ReaderAdapter).isFullScreen = value (binding.recyclerview.adapter as ReaderAdapter).isFullScreen = value
} }
private lateinit var cache: Cache private lateinit var cache: Cache
@@ -118,9 +116,12 @@ class ReaderActivity : BaseActivity() {
private var eyeType: Eye? = null private var eyeType: Eye? = null
private var eyeTime: Long = 0L private var eyeTime: Long = 0L
private lateinit var binding: ReaderActivityBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reader) binding = ReaderActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
title = getString(R.string.reader_loading) title = getString(R.string.reader_loading)
supportActionBar?.setDisplayHomeAsUpEnabled(false) supportActionBar?.setDisplayHomeAsUpEnabled(false)
@@ -178,17 +179,19 @@ class ReaderActivity : BaseActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId) { when(item.itemId) {
R.id.reader_menu_page_indicator -> { R.id.reader_menu_page_indicator -> {
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false) // TODO: Switch to DialogFragment
with(view.dialog_number_picker) { val binding = NumberpickerDialogBinding.inflate(layoutInflater, binding.root, false)
with(binding.numberPicker) {
minValue = 1 minValue = 1
maxValue = cache.metadata.reader?.galleryInfo?.files?.size ?: 0 maxValue = cache.metadata.reader?.galleryInfo?.files?.size ?: 0
value = currentPage value = currentPage
} }
val dialog = AlertDialog.Builder(this).apply { val dialog = AlertDialog.Builder(this).apply {
setView(view) setView(binding.root)
}.create() }.create()
view.dialog_ok.setOnClickListener { binding.okButton.setOnClickListener {
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(view.dialog_number_picker.value-1, 0) (this@ReaderActivity.binding.recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(binding.numberPicker.value-1, 0)
dialog.dismiss() dialog.dismiss()
} }
@@ -258,12 +261,12 @@ class ReaderActivity : BaseActivity() {
//currentPage is 1-based //currentPage is 1-based
return when(keyCode) { return when(keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> { KeyEvent.KEYCODE_VOLUME_UP -> {
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-2, 0) (binding.recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-2, 0)
true true
} }
KeyEvent.KEYCODE_VOLUME_DOWN -> { KeyEvent.KEYCODE_VOLUME_DOWN -> {
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(currentPage, 0) (binding.recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(currentPage, 0)
true true
} }
@@ -285,21 +288,21 @@ class ReaderActivity : BaseActivity() {
if (downloader.progress[galleryID]?.isEmpty() == true) { //Gallery not found if (downloader.progress[galleryID]?.isEmpty() == true) { //Gallery not found
update = false update = false
Snackbar Snackbar
.make(reader_layout, R.string.reader_failed_to_find_gallery, Snackbar.LENGTH_INDEFINITE) .make(binding.root, R.string.reader_failed_to_find_gallery, Snackbar.LENGTH_INDEFINITE)
.show() .show()
return@launch return@launch
} }
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0 binding.downloadProgressbar.max = binding.recyclerview.adapter?.itemCount ?: 0
reader_download_progressbar.progress = binding.downloadProgressbar.progress =
downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0 downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0
if (title == getString(R.string.reader_loading)) { if (title == getString(R.string.reader_loading)) {
val reader = cache.metadata.reader val reader = cache.metadata.reader
if (reader != null) { if (reader != null) {
with(reader_recyclerview.adapter as ReaderAdapter) { with(binding.recyclerview.adapter as ReaderAdapter) {
this.reader = reader this.reader = reader
notifyDataSetChanged() notifyDataSetChanged()
} }
@@ -320,7 +323,7 @@ class ReaderActivity : BaseActivity() {
} }
if (downloader.isCompleted(galleryID)) { //Download finished if (downloader.isCompleted(galleryID)) { //Download finished
reader_download_progressbar.visibility = View.GONE binding.downloadProgressbar.visibility = View.GONE
animateDownloadFAB(false) animateDownloadFAB(false)
} }
@@ -329,7 +332,7 @@ class ReaderActivity : BaseActivity() {
} }
private fun initView() { private fun initView() {
with(reader_recyclerview) { with(binding.recyclerview) {
adapter = ReaderAdapter(this@ReaderActivity, galleryID).apply { adapter = ReaderAdapter(this@ReaderActivity, galleryID).apply {
onItemClickListener = { onItemClickListener = {
if (isScroll) { if (isScroll) {
@@ -339,7 +342,7 @@ class ReaderActivity : BaseActivity() {
scrollMode(false) scrollMode(false)
fullscreen(true) fullscreen(true)
} else { } else {
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage, 0) //Moves to next page because currentPage is 1-based indexing (binding.recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage, 0) //Moves to next page because currentPage is 1-based indexing
} }
} }
} }
@@ -349,9 +352,9 @@ class ReaderActivity : BaseActivity() {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
if (dy < 0) if (dy < 0)
this@ReaderActivity.reader_fab.showMenuButton(true) binding.fab.showMenuButton(true)
else if (dy > 0) else if (dy > 0)
this@ReaderActivity.reader_fab.hideMenuButton(true) binding.fab.hideMenuButton(true)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager val layoutManager = recyclerView.layoutManager as LinearLayoutManager
@@ -363,7 +366,7 @@ class ReaderActivity : BaseActivity() {
}) })
} }
with(reader_fab_download) { with(binding.downloadFab) {
animateDownloadFAB(DownloadManager.getInstance(this@ReaderActivity).getDownloadFolder(galleryID) != null) //If download in progress, animate button animateDownloadFAB(DownloadManager.getInstance(this@ReaderActivity).getDownloadFolder(galleryID) != null) //If download in progress, animate button
setOnClickListener { setOnClickListener {
@@ -380,14 +383,14 @@ class ReaderActivity : BaseActivity() {
} }
} }
with(reader_fab_retry) { with(binding.retryFab) {
setImageResource(R.drawable.refresh) setImageResource(R.drawable.refresh)
setOnClickListener { setOnClickListener {
DownloadService.download(context, galleryID) DownloadService.download(context, galleryID)
} }
} }
with(reader_fab_auto) { with(binding.autoFab) {
setImageResource(R.drawable.eye_white) setImageResource(R.drawable.eye_white)
setOnClickListener { setOnClickListener {
when { when {
@@ -407,13 +410,13 @@ class ReaderActivity : BaseActivity() {
} }
} }
with(reader_fab_fullscreen) { with(binding.fullscreenFab) {
setImageResource(R.drawable.ic_fullscreen) setImageResource(R.drawable.ic_fullscreen)
setOnClickListener { setOnClickListener {
isFullscreen = true isFullscreen = true
fullscreen(isFullscreen) fullscreen(isFullscreen)
this@ReaderActivity.reader_fab.close(true) binding.fab.close(true)
} }
} }
} }
@@ -423,8 +426,8 @@ class ReaderActivity : BaseActivity() {
if (isFullscreen) { if (isFullscreen) {
flags = flags or WindowManager.LayoutParams.FLAG_FULLSCREEN flags = flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
supportActionBar?.hide() supportActionBar?.hide()
this@ReaderActivity.reader_fab.visibility = View.INVISIBLE binding.fab.visibility = View.INVISIBLE
this@ReaderActivity.scroller.let { binding.scroller.let {
it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_height) it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_height)
it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_width) it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_width)
it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb_horizontal) it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb_horizontal)
@@ -433,8 +436,8 @@ class ReaderActivity : BaseActivity() {
} else { } else {
flags = flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv() flags = flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
supportActionBar?.show() supportActionBar?.show()
this@ReaderActivity.reader_fab.visibility = View.VISIBLE binding.fab.visibility = View.VISIBLE
this@ReaderActivity.scroller.let { binding.scroller.let {
it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_width) it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_width)
it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_height) it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_height)
it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb) it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb)
@@ -445,27 +448,27 @@ class ReaderActivity : BaseActivity() {
window.attributes = this window.attributes = this
} }
reader_recyclerview.adapter = reader_recyclerview.adapter // Force to redraw binding.recyclerview.adapter = binding.recyclerview.adapter // Force to redraw
} }
private fun scrollMode(isScroll: Boolean) { private fun scrollMode(isScroll: Boolean) {
if (isScroll) { if (isScroll) {
snapHelper.attachToRecyclerView(null) snapHelper.attachToRecyclerView(null)
reader_recyclerview.layoutManager = LinearLayoutManager(this) binding.recyclerview.layoutManager = LinearLayoutManager(this)
} else { } else {
snapHelper.attachToRecyclerView(reader_recyclerview) snapHelper.attachToRecyclerView(binding.recyclerview)
reader_recyclerview.layoutManager = object: LinearLayoutManager(this, HORIZONTAL, Preferences["rtl", false]) { binding.recyclerview.layoutManager = object: LinearLayoutManager(this, HORIZONTAL, Preferences["rtl", false]) {
override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) { override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) {
extraLayoutSpace.fill(600) extraLayoutSpace.fill(600)
} }
} }
} }
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) (binding.recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
} }
private fun animateDownloadFAB(animate: Boolean) { private fun animateDownloadFAB(animate: Boolean) {
with(reader_fab_download) { with(binding.downloadFab) {
if (animate) { if (animate) {
val icon = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_downloading) val icon = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_downloading)
@@ -494,7 +497,7 @@ class ReaderActivity : BaseActivity() {
} }
val cameraCallback: (List<Face>) -> Unit = callback@{ faces -> val cameraCallback: (List<Face>) -> Unit = callback@{ faces ->
eye_card.dot.let { binding.eyeCard.dot.let {
it.visibility = View.VISIBLE it.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
delay(50) delay(50)
@@ -504,9 +507,9 @@ class ReaderActivity : BaseActivity() {
if (faces.size != 1) if (faces.size != 1)
ContextCompat.getDrawable(this, R.drawable.eye_off).let { ContextCompat.getDrawable(this, R.drawable.eye_off).let {
with(eye_card) { with(binding.eyeCard) {
left_eye.setImageDrawable(it) leftEye.setImageDrawable(it)
right_eye.setImageDrawable(it) rightEye.setImageDrawable(it)
} }
return@callback return@callback
@@ -517,16 +520,16 @@ class ReaderActivity : BaseActivity() {
faces[0].leftEyeOpenProbability?.let { it > 0.4 } == true faces[0].leftEyeOpenProbability?.let { it > 0.4 } == true
) )
with(eye_card) { with(binding.eyeCard) {
left_eye.setImageDrawable( leftEye.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, leftEye.context,
if (left) R.drawable.eye else R.drawable.eye_closed if (left) R.drawable.eye else R.drawable.eye_closed
) )
) )
right_eye.setImageDrawable( rightEye.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, rightEye.context,
if (right) R.drawable.eye else R.drawable.eye_closed if (right) R.drawable.eye else R.drawable.eye_closed
) )
) )
@@ -553,7 +556,7 @@ class ReaderActivity : BaseActivity() {
} }
if (eyeType != null && System.currentTimeMillis() - eyeTime > 100) { if (eyeType != null && System.currentTimeMillis() - eyeTime > 100) {
(this@ReaderActivity.reader_recyclerview.layoutManager as LinearLayoutManager).let { (binding.recyclerview.layoutManager as LinearLayoutManager).let {
it.scrollToPositionWithOffset(when(eyeType!!) { it.scrollToPositionWithOffset(when(eyeType!!) {
Eye.RIGHT -> { Eye.RIGHT -> {
if (it.reverseLayout) currentPage - 2 else currentPage if (it.reverseLayout) currentPage - 2 else currentPage
@@ -569,11 +572,11 @@ class ReaderActivity : BaseActivity() {
} }
private fun toggleCamera() { private fun toggleCamera() {
val eyes = this@ReaderActivity.eye_card val eyes = binding.eyeCard.root
when (camera) { when (camera) {
null -> { null -> {
reader_fab_auto.labelText = getString(R.string.reader_fab_auto_cancel) binding.autoFab.labelText = getString(R.string.reader_fab_auto_cancel)
reader_fab_auto.setImageResource(R.drawable.eye_off_white) binding.autoFab.setImageResource(R.drawable.eye_off_white)
eyes.apply { eyes.apply {
visibility = View.VISIBLE visibility = View.VISIBLE
TranslateAnimation(0F, 0F, -100F, 0F).apply { TranslateAnimation(0F, 0F, -100F, 0F).apply {
@@ -586,8 +589,8 @@ class ReaderActivity : BaseActivity() {
cameraEnabled = true cameraEnabled = true
} }
else -> { else -> {
reader_fab_auto.labelText = getString(R.string.reader_fab_auto) binding.autoFab.labelText = getString(R.string.reader_fab_auto)
reader_fab_auto.setImageResource(R.drawable.eye_white) binding.autoFab.setImageResource(R.drawable.eye_white)
eyes.apply { eyes.apply {
TranslateAnimation(0F, 0F, 0F, -100F).apply { TranslateAnimation(0F, 0F, 0F, -100F).apply {
duration = 500 duration = 500

View File

@@ -18,30 +18,30 @@
package xyz.quaver.pupil.ui.dialog package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_default_query.* import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_default_query.view.*
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding
import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
class DefaultQueryDialog(context : Context) : AlertDialog(context) { class DefaultQueryDialog : DialogFragment() {
private val languages = context.resources.getStringArray(R.array.languages).map { private val languages: Map<String, String> by lazy {
it.split("|").let { split -> requireContext().resources.getStringArray(R.array.languages).map {
Pair(split[0], split[1]) it.split("|").let { split ->
} Pair(split[0], split[1])
}.toMap() }
private val reverseLanguages = languages.entries.associate { (k, v) -> v to k } }.toMap()
}
private val reverseLanguages: Map<String, String> by lazy {
languages.entries.associate { (k, v) -> v to k }
}
private val excludeBL = "-male:yaoi" private val excludeBL = "-male:yaoi"
private val excludeGuro = listOf("-female:guro", "-male:guro") private val excludeGuro = listOf("-female:guro", "-male:guro")
@@ -49,46 +49,15 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null
@SuppressLint("InflateParams") private var _binding: DefaultQueryDialogBinding? = null
override fun onCreate(savedInstanceState: Bundle?) { private val binding get() = _binding!!
setTitle(R.string.default_query_dialog_title)
setView(build())
setButton(Dialog.BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ ->
val newTags = Tags.parse(default_query_dialog_edittext.text.toString())
with(default_query_dialog_language_selector) { private fun initView() {
if (selectedItemPosition != 0)
newTags.add("language:${reverseLanguages[selectedItem]}")
}
if (default_query_dialog_BL_checkbox.isChecked)
newTags.add(excludeBL)
if (default_query_dialog_guro_checkbox.isChecked)
excludeGuro.forEach { tag ->
newTags.add(tag)
}
if (default_query_dialog_loli_checkbox.isChecked)
excludeLoli.forEach { tag ->
newTags.add(tag)
}
onPositiveButtonClickListener?.invoke(newTags)
}
super.onCreate(savedInstanceState)
}
@SuppressLint("InflateParams")
private fun build() : View {
val tags = Tags.parse( val tags = Tags.parse(
Preferences["default_query"] Preferences["default_query"]
) )
val view = LayoutInflater.from(context).inflate(R.layout.dialog_default_query, null) with(binding.languageSelector) {
with(view.default_query_dialog_language_selector) {
adapter = adapter =
ArrayAdapter( ArrayAdapter(
context, context,
@@ -111,13 +80,13 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
} }
} }
with(view.default_query_dialog_BL_checkbox) { with(binding.BLCheckbox) {
isChecked = tags.contains(excludeBL) isChecked = tags.contains(excludeBL)
if (tags.contains(excludeBL)) if (tags.contains(excludeBL))
tags.remove(excludeBL) tags.remove(excludeBL)
} }
with(view.default_query_dialog_guro_checkbox) { with(binding.guroCheckbox) {
isChecked = excludeGuro.all { tags.contains(it) } isChecked = excludeGuro.all { tags.contains(it) }
if (excludeGuro.all { tags.contains(it) }) if (excludeGuro.all { tags.contains(it) })
excludeGuro.forEach { excludeGuro.forEach {
@@ -125,7 +94,7 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
} }
} }
with(view.default_query_dialog_loli_checkbox) { with(binding.loliCheckbox) {
isChecked = excludeLoli.all { tags.contains(it) } isChecked = excludeLoli.all { tags.contains(it) }
if (excludeLoli.all { tags.contains(it) }) if (excludeLoli.all { tags.contains(it) })
excludeLoli.forEach { excludeLoli.forEach {
@@ -133,7 +102,7 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
} }
} }
with(view.default_query_dialog_edittext) { with(binding.edittext) {
setText(tags.toString(), android.widget.TextView.BufferType.EDITABLE) setText(tags.toString(), android.widget.TextView.BufferType.EDITABLE)
addTextChangedListener(object : TextWatcher { addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged( override fun beforeTextChanged(
@@ -158,8 +127,45 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
} }
}) })
} }
}
return view override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = DefaultQueryDialogBinding.inflate(layoutInflater)
initView()
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]}")
}
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()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
} }
} }

View File

@@ -18,17 +18,15 @@
package xyz.quaver.pupil.ui.dialog package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.dialog_download_folder_name.view.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.DownloadFolderNameDialogBinding
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.formatDownloadFolder import xyz.quaver.pupil.util.formatDownloadFolder
@@ -37,38 +35,48 @@ import xyz.quaver.pupil.util.formatMap
class DownloadFolderNameDialogFragment : DialogFragment() { class DownloadFolderNameDialogFragment : DialogFragment() {
@SuppressLint("InflateParams") private var _binding: DownloadFolderNameDialogBinding? = null
private fun build(): View { private val binding get() = _binding!!
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = DownloadFolderNameDialogBinding.inflate(layoutInflater)
initView()
return Dialog(requireContext()).apply {
setContentView(binding.root)
window?.attributes?.width = ViewGroup.LayoutParams.MATCH_PARENT
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
private fun initView() {
val galleryID = Cache.instances.let { if (it.size == 0) 1199708 else it.keys.elementAt((0 until it.size).random()) } val galleryID = Cache.instances.let { if (it.size == 0) 1199708 else it.keys.elementAt((0 until it.size).random()) }
val galleryBlock = runBlocking { val galleryBlock = runBlocking {
Cache.getInstance(requireContext(), galleryID).getGalleryBlock() Cache.getInstance(requireContext(), galleryID).getGalleryBlock()
} }
return layoutInflater.inflate(R.layout.dialog_download_folder_name, null).apply { binding.message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolder() ?: "")
message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolder() ?: "") binding.edittext.setText(Preferences["download_folder_name", "[-id-] -title-"])
edittext.setText(Preferences["download_folder_name", "[-id-] -title-"]) binding.edittext.addTextChangedListener {
edittext.addTextChangedListener { binding.message.text = requireContext().getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolderTest(it.toString()) ?: "")
message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolderTest(it.toString()) ?: "") }
binding.okButton.setOnClickListener {
val newValue = binding.edittext.text.toString()
if ((newValue as? String)?.contains("/") != false) {
Snackbar.make(binding.root, R.string.settings_invalid_download_folder_name, Snackbar.LENGTH_SHORT).show()
return@setOnClickListener
} }
ok_button.setOnClickListener {
val newValue = edittext.text.toString()
if ((newValue as? String)?.contains("/") != false) { Preferences["download_folder_name"] = binding.edittext.text.toString()
Snackbar.make(this, R.string.settings_invalid_download_folder_name, Snackbar.LENGTH_SHORT).show()
return@setOnClickListener
}
Preferences["download_folder_name"] = edittext.text.toString() dismiss()
dismiss()
}
} }
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
Dialog(requireContext()).apply {
setContentView(build())
window?.attributes?.width = ViewGroup.LayoutParams.MATCH_PARENT
}
} }

View File

@@ -18,27 +18,24 @@
package xyz.quaver.pupil.ui.dialog package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.dialog_download_folder_name.view.*
import kotlinx.android.synthetic.main.item_download_folder.view.*
import net.rdrei.android.dirchooser.DirectoryChooserActivity import net.rdrei.android.dirchooser.DirectoryChooserActivity
import net.rdrei.android.dirchooser.DirectoryChooserConfig import net.rdrei.android.dirchooser.DirectoryChooserConfig
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.toFile import xyz.quaver.io.util.toFile
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.DownloadLocationDialogBinding
import xyz.quaver.pupil.databinding.DownloadLocationItemBinding
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.byteToString import xyz.quaver.pupil.util.byteToString
import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.downloader.DownloadManager
@@ -46,24 +43,25 @@ import xyz.quaver.pupil.util.migrate
import java.io.File import java.io.File
class DownloadLocationDialogFragment : DialogFragment() { class DownloadLocationDialogFragment : DialogFragment() {
private val entries = mutableMapOf<File?, View>()
private var _binding: DownloadLocationDialogBinding? = null
private val binding get() = _binding!!
private val entries = mutableMapOf<File?, DownloadLocationItemBinding>()
private val requestDownloadFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { private val requestDownloadFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) { if (it.resultCode == Activity.RESULT_OK) {
val activity = activity ?: return@registerForActivityResult
val context = context ?: return@registerForActivityResult val context = context ?: return@registerForActivityResult
val dialog = dialog ?: return@registerForActivityResult val dialog = dialog ?: return@registerForActivityResult
it.data?.data?.also { uri -> it.data?.data?.also { uri ->
val takeFlags: Int = val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.intent.flags and
(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
context.contentResolver.takePersistableUriPermission(uri, takeFlags) context.contentResolver.takePersistableUriPermission(uri, takeFlags)
if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) { if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) {
entries[null]?.location_available?.text = uri.toFile(context)?.canonicalPath entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath
Preferences["download_folder"] = uri.toString() Preferences["download_folder"] = uri.toString()
} else { } else {
Snackbar.make( Snackbar.make(
@@ -75,7 +73,7 @@ class DownloadLocationDialogFragment : DialogFragment() {
val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder } val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
entries[key]!!.button.isChecked = true entries[key]!!.button.isChecked = true
if (key == null) entries[key]!!.location_available.text = downloadFolder if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
} }
} }
} }
@@ -98,49 +96,44 @@ class DownloadLocationDialogFragment : DialogFragment() {
val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder } val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
entries[key]!!.button.isChecked = true entries[key]!!.button.isChecked = true
if (key == null) entries[key]!!.location_available.text = downloadFolder if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
} }
else { else {
entries[null]?.location_available?.text = directory entries[null]?.locationAvailable?.text = directory
Preferences["download_folder"] = File(directory).toURI().toString() Preferences["download_folder"] = File(directory).toURI().toString()
} }
} }
} }
@SuppressLint("InflateParams") private fun initView() {
private fun build() : View? { val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null)
val context = context ?: return null
val view = layoutInflater.inflate(R.layout.dialog_download_folder, null) as LinearLayout
val externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null)
externalFilesDirs.forEachIndexed { index, dir -> externalFilesDirs.forEachIndexed { index, dir ->
dir ?: return@forEachIndexed dir ?: return@forEachIndexed
view.addView(layoutInflater.inflate(R.layout.item_download_folder, view, false).apply { DownloadLocationItemBinding.inflate(layoutInflater, binding.root, true).apply {
location_type.text = context.getString(when (index) { locationType.text = requireContext().getString(when (index) {
0 -> R.string.settings_download_folder_internal 0 -> R.string.settings_download_folder_internal
else -> R.string.settings_download_folder_removable else -> R.string.settings_download_folder_removable
}) })
location_available.text = context.getString( locationAvailable.text = requireContext().getString(
R.string.settings_download_folder_available, R.string.settings_download_folder_available,
byteToString(dir.freeSpace) byteToString(dir.freeSpace)
) )
setOnClickListener { root.setOnClickListener {
entries.values.forEach { entries.values.forEach { _ ->
it.button.isChecked = false button.isChecked = false
} }
button.performClick() button.performClick()
Preferences["download_folder"] = dir.toUri().toString() Preferences["download_folder"] = dir.toUri().toString()
} }
entries[dir] = this entries[dir] = this
}) }
} }
view.addView(layoutInflater.inflate(R.layout.item_download_folder, view, false).apply { DownloadLocationItemBinding.inflate(layoutInflater, binding.root, true).apply {
location_type.text = context.getString(R.string.settings_download_folder_custom) locationType.text = requireContext().getString(R.string.settings_download_folder_custom)
setOnClickListener { root.setOnClickListener {
entries.values.forEach { entries.values.forEach {
it.button.isChecked = false it.button.isChecked = false
} }
@@ -166,31 +159,35 @@ class DownloadLocationDialogFragment : DialogFragment() {
} }
} }
entries[null] = this entries[null] = this
}) }
val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath val downloadFolder = DownloadManager.getInstance(requireContext()).downloadFolder.canonicalPath
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder } val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
entries[key]!!.button.isChecked = true entries[key]!!.button.isChecked = true
if (key == null) entries[key]!!.location_available.text = downloadFolder if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
return view
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireContext()) _binding = DownloadLocationDialogBinding.inflate(layoutInflater)
builder initView()
.setTitle(R.string.settings_download_folder)
.setView(build()) return AlertDialog.Builder(requireContext()).apply {
.setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ -> setTitle(R.string.settings_download_folder)
setView(binding.root)
setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ ->
if (Preferences["download_folder", ""].isEmpty()) if (Preferences["download_folder", ""].isEmpty())
Preferences["download_folder"] = context?.getExternalFilesDir(null)?.toUri()?.toString() ?: "" Preferences["download_folder"] = context.getExternalFilesDir(null)?.toUri()?.toString() ?: ""
DownloadManager.getInstance(requireContext()).migrate() DownloadManager.getInstance(requireContext()).migrate()
} }
isCancelable = false isCancelable = false
}.create()
}
return builder.create() override fun onDestroy() {
super.onDestroy()
_binding = null
} }
} }

View File

@@ -22,7 +22,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout.LayoutParams import android.widget.LinearLayout.LayoutParams
@@ -32,10 +31,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.dialog_gallery.*
import kotlinx.android.synthetic.main.dialog_gallery_details.view.*
import kotlinx.android.synthetic.main.dialog_gallery_dotindicator.view.*
import kotlinx.android.synthetic.main.item_gallery_details.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -45,6 +40,7 @@ import xyz.quaver.hitomi.getGallery
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.GalleryBlockAdapter import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.adapters.ThumbnailPageAdapter import xyz.quaver.pupil.adapters.ThumbnailPageAdapter
import xyz.quaver.pupil.databinding.*
import xyz.quaver.pupil.favoriteTags import xyz.quaver.pupil.favoriteTags
import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.ui.ReaderActivity
@@ -59,9 +55,12 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>() val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
private lateinit var binding: GalleryDialogBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_gallery) binding = GalleryDialogBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.attributes.apply { window?.attributes.apply {
this ?: return@apply this ?: return@apply
@@ -70,7 +69,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
height = LayoutParams.MATCH_PARENT height = LayoutParams.MATCH_PARENT
} }
with(gallery_fab) { with(binding.fab) {
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.arrow_right)) setImageDrawable(ContextCompat.getDrawable(context, R.drawable.arrow_right))
setOnClickListener { setOnClickListener {
context.startActivity(Intent(context, ReaderActivity::class.java).apply { context.startActivity(Intent(context, ReaderActivity::class.java).apply {
@@ -83,12 +82,12 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
try { try {
val gallery = getGallery(galleryID) val gallery = getGallery(galleryID)
gallery_cover.post { launch (Dispatchers.Main) {
gallery_progressbar.visibility = View.GONE binding.progressbar.visibility = View.GONE
gallery_title.text = gallery.title binding.title.text = gallery.title
gallery_artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() } binding.artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() }
with(gallery_type) { with(binding.type) {
text = gallery.type.wordCapitalize() text = gallery.type.wordCapitalize()
setOnClickListener { setOnClickListener {
gallery.type.let { gallery.type.let {
@@ -105,14 +104,14 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
} }
gallery_cover.showImage(Uri.parse(gallery.cover)) binding.cover.showImage(Uri.parse(gallery.cover))
addDetails(gallery) addDetails(gallery)
addThumbnails(gallery) addThumbnails(gallery)
addRelated(gallery) addRelated(gallery)
} }
} catch (e: Exception) { } catch (e: Exception) {
Snackbar.make(gallery_layout, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).apply { Snackbar.make(binding.root, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).apply {
if (Locale.getDefault().language == "ko") if (Locale.getDefault().language == "ko")
setAction(context.getText(R.string.https_text)) { setAction(context.getText(R.string.https_text)) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.https)))) context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.https))))
@@ -123,10 +122,8 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
private fun addDetails(gallery: Gallery) { private fun addDetails(gallery: Gallery) {
val inflater = LayoutInflater.from(context) GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply {
type.setText(R.string.gallery_details)
inflater.inflate(R.layout.dialog_gallery_details, gallery_contents, false).apply {
gallery_details.setText(R.string.gallery_details)
listOf( listOf(
R.string.gallery_artists, R.string.gallery_artists,
@@ -163,13 +160,13 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
) )
).filter { ).filter {
(_, content) -> content.isNotEmpty() (_, content) -> content.isNotEmpty()
}.forEach { (title, content) -> }.forEach { (title, content) ->
inflater.inflate(R.layout.item_gallery_details, gallery_details_contents, false).apply { GalleryDialogTagsBinding.inflate(layoutInflater, contents, true).apply {
gallery_details_type.setText(title) type.setText(title)
content.forEach { tag -> content.forEach { tag ->
gallery_details_tags.addView( tags.addView(
TagChip(context, tag).apply { TagChip(context, tag).apply {
setOnClickListener { setOnClickListener {
onChipClickedHandler.forEach { handler -> onChipClickedHandler.forEach { handler ->
@@ -179,42 +176,34 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
) )
} }
}.let {
gallery_details_contents.addView(it)
} }
} }
}.let {
gallery_contents.addView(it)
} }
} }
private fun addThumbnails(gallery: Gallery) { private fun addThumbnails(gallery: Gallery) {
val inflater = LayoutInflater.from(context) GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply {
type.setText(R.string.gallery_thumbnails)
inflater.inflate(R.layout.dialog_gallery_details, gallery_contents, false).apply {
gallery_details.setText(R.string.gallery_thumbnails)
val pager = ViewPager2(context).apply { val pager = ViewPager2(context).apply {
adapter = ThumbnailPageAdapter(gallery.thumbnails) adapter = ThumbnailPageAdapter(gallery.thumbnails)
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
} }
gallery_details_contents.addView( contents.addView(
pager, pager,
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
) )
LayoutInflater.from(context).inflate(R.layout.dialog_gallery_dotindicator, gallery_details_contents) // TODO: Change to direct allocation
GalleryDialogDotindicatorBinding.inflate(layoutInflater, contents, true).apply {
gallery_dotindicator.setViewPager2(pager) dotindicator.setViewPager2(pager)
}.let { }
gallery_contents.addView(it)
} }
} }
private fun addRelated(gallery: Gallery) { private fun addRelated(gallery: Gallery) {
val inflater = LayoutInflater.from(context) val galleries = mutableListOf<Int>()
val galleries = ArrayList<Int>()
val adapter = GalleryBlockAdapter(galleries).apply { val adapter = GalleryBlockAdapter(galleries).apply {
onChipClickedHandler.add { tag -> onChipClickedHandler.add { tag ->
@@ -224,10 +213,10 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
} }
inflater.inflate(R.layout.dialog_gallery_details, gallery_contents, false).apply { GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply {
gallery_details.setText(R.string.gallery_related) type.setText(R.string.gallery_related)
RecyclerView(context).apply { contents.addView(RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
this.adapter = adapter this.adapter = adapter
@@ -247,22 +236,18 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
true true
} }
} }
}.let { })
gallery_details_contents.addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
}
}.let {
gallery_contents.addView(it)
}
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
gallery.related.forEach { galleryID -> gallery.related.forEach { galleryID ->
Cache.getInstance(context, galleryID).getGalleryBlock()?.let { Cache.getInstance(context, galleryID).getGalleryBlock()?.let {
galleries.add(galleryID) galleries.add(galleryID)
}
} }
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
}
} }
} }
} }

View File

@@ -18,23 +18,19 @@
package xyz.quaver.pupil.ui.dialog package xyz.quaver.pupil.ui.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_proxy.view.*
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.clientBuilder import xyz.quaver.pupil.clientBuilder
import xyz.quaver.pupil.clientHolder import xyz.quaver.pupil.clientHolder
import xyz.quaver.pupil.databinding.ProxyDialogBinding
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.ProxyInfo import xyz.quaver.pupil.util.ProxyInfo
import xyz.quaver.pupil.util.getProxyInfo import xyz.quaver.pupil.util.getProxyInfo
@@ -43,33 +39,35 @@ import java.net.Proxy
class ProxyDialog(context: Context) : AlertDialog(context) { class ProxyDialog(context: Context) : AlertDialog(context) {
override fun onCreate(savedInstanceState: Bundle?) { private lateinit var binding: ProxyDialogBinding
setView(build())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ProxyDialogBinding.inflate(layoutInflater)
setView(binding.root)
initView()
} }
@SuppressLint("InflateParams") private fun initView() {
private fun build() : View {
val proxyInfo = getProxyInfo() val proxyInfo = getProxyInfo()
val view = LayoutInflater.from(context).inflate(R.layout.dialog_proxy, null)
val enabler = { enable: Boolean -> val enabler = { enable: Boolean ->
view?.proxy_addr?.isEnabled = enable binding.addr.isEnabled = enable
view?.proxy_port?.isEnabled = enable binding.port.isEnabled = enable
view?.proxy_username?.isEnabled = enable binding.username.isEnabled = enable
view?.proxy_password?.isEnabled = enable binding.password.isEnabled = enable
if (!enable) { if (!enable) {
view?.proxy_addr?.text = null binding.addr.text = null
view?.proxy_port?.text = null binding.port.text = null
view?.proxy_username?.text = null binding.username.text = null
view?.proxy_password?.text = null binding.password.text = null
} }
} }
with(view.proxy_type_selector) { with(binding.typeSelector) {
adapter = ArrayAdapter( adapter = ArrayAdapter(
context, context,
android.R.layout.simple_spinner_dropdown_item, android.R.layout.simple_spinner_dropdown_item,
@@ -87,29 +85,29 @@ class ProxyDialog(context: Context) : AlertDialog(context) {
} }
} }
view.proxy_addr.setText(proxyInfo.host) binding.addr.setText(proxyInfo.host)
view.proxy_port.setText(proxyInfo.port?.toString()) binding.port.setText(proxyInfo.port?.toString())
view.proxy_username.setText(proxyInfo.username) binding.username.setText(proxyInfo.username)
view.proxy_password.setText(proxyInfo.password) binding.password.setText(proxyInfo.password)
enabler.invoke(proxyInfo.type != Proxy.Type.DIRECT) enabler.invoke(proxyInfo.type != Proxy.Type.DIRECT)
view.proxy_cancel.setOnClickListener { binding.cancelButton.setOnClickListener {
dismiss() dismiss()
} }
view.proxy_ok.setOnClickListener { binding.okButton.setOnClickListener {
val type = Proxy.Type.values()[view.proxy_type_selector.selectedItemPosition] val type = Proxy.Type.values()[binding.typeSelector.selectedItemPosition]
val addr = view.proxy_addr.text?.toString() val addr = binding.addr.text?.toString()
val port = view.proxy_port.text?.toString()?.toIntOrNull() val port = binding.port.text?.toString()?.toIntOrNull()
val username = view.proxy_username.text?.toString() val username = binding.username.text?.toString()
val password = view.proxy_password.text?.toString() val password = binding.password.text?.toString()
if (type != Proxy.Type.DIRECT) { if (type != Proxy.Type.DIRECT) {
if (addr == null || addr.isEmpty()) if (addr == null || addr.isEmpty())
view.proxy_addr.error = context.getText(R.string.proxy_dialog_error) binding.addr.error = context.getText(R.string.proxy_dialog_error)
if (port == null) if (port == null)
view.proxy_port.error = context.getText(R.string.proxy_dialog_error) binding.port.error = context.getText(R.string.proxy_dialog_error)
if (addr == null || addr.isEmpty() || port == null) if (addr == null || addr.isEmpty() || port == null)
return@setOnClickListener return@setOnClickListener
@@ -126,8 +124,6 @@ class ProxyDialog(context: Context) : AlertDialog(context) {
dismiss() dismiss()
} }
return view
} }
} }

View File

@@ -94,7 +94,12 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
} }
if (dir.exists()) if (dir.exists())
dir.listFiles()?.forEach { (it as? FileX)?.deleteRecursively() } dir.listFiles()?.forEach {
when (it) {
is FileX -> it.deleteRecursively()
else -> it.deleteRecursively()
}
}
job = launch { job = launch {
var size = 0L var size = 0L

View File

@@ -24,30 +24,34 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.andrognito.pinlockview.PinLockListener import com.andrognito.pinlockview.PinLockListener
import kotlinx.android.synthetic.main.fragment_pin_lock.view.* import xyz.quaver.pupil.databinding.PinLockFragmentBinding
import xyz.quaver.pupil.R
class PINLockFragment : Fragment(), PinLockListener { class PINLockFragment : Fragment() {
private var _binding: PinLockFragmentBinding? = null
val binding get() = _binding!!
var onPINEntered: ((String) -> Unit)? = null var onPINEntered: ((String) -> Unit)? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_pin_lock, container, false).apply { _binding = PinLockFragmentBinding.inflate(inflater, container, false)
pin_lock_view.attachIndicatorDots(indicator_dots)
pin_lock_view.setPinLockListener(this@PINLockFragment) binding.pinLockView.attachIndicatorDots(binding.indicatorDots)
} binding.pinLockView.setPinLockListener(object: PinLockListener {
override fun onComplete(p0: String?) {
onPINEntered?.invoke(p0 ?: "")
}
override fun onEmpty() {}
override fun onPinChange(p0: Int, p1: String?) {}
})
return binding.root
} }
override fun onComplete(pin: String?) { override fun onDestroy() {
onPINEntered?.invoke(pin!!) super.onDestroy()
} _binding = null
override fun onEmpty() {
}
override fun onPinChange(pinLength: Int, intermediatePin: String?) {
} }
} }

View File

@@ -26,38 +26,36 @@ import androidx.fragment.app.Fragment
import com.andrognito.patternlockview.PatternLockView import com.andrognito.patternlockview.PatternLockView
import com.andrognito.patternlockview.listener.PatternLockViewListener import com.andrognito.patternlockview.listener.PatternLockViewListener
import com.andrognito.patternlockview.utils.PatternLockUtils import com.andrognito.patternlockview.utils.PatternLockUtils
import kotlinx.android.synthetic.main.fragment_pattern_lock.* import xyz.quaver.pupil.databinding.PatternLockFragmentBinding
import kotlinx.android.synthetic.main.fragment_pattern_lock.view.*
import xyz.quaver.pupil.R
class PatternLockFragment : Fragment(), PatternLockViewListener { class PatternLockFragment : Fragment() {
private var _binding: PatternLockFragmentBinding? = null
val binding get() = _binding!!
var onPatternDrawn: ((String) -> Unit)? = null var onPatternDrawn: ((String) -> Unit)? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
return inflater.inflate(R.layout.fragment_pattern_lock, container, false).apply { _binding = PatternLockFragmentBinding.inflate(inflater, container, false)
lock_pattern_view.addPatternLockListener(this@PatternLockFragment) binding.patternLockView.addPatternLockListener(object: PatternLockViewListener {
} override fun onComplete(pattern: MutableList<PatternLockView.Dot>?) {
val password = PatternLockUtils.patternToMD5(binding.patternLockView, pattern)
onPatternDrawn?.invoke(password)
}
override fun onCleared() {}
override fun onProgress(progressPattern: MutableList<PatternLockView.Dot>?) {}
override fun onStarted() {}
})
return binding.root
} }
override fun onCleared() { override fun onDestroy() {
super.onDestroy()
} _binding = null
override fun onComplete(pattern: MutableList<PatternLockView.Dot>?) {
val password = PatternLockUtils.patternToMD5(lock_pattern_view, pattern)
onPatternDrawn?.invoke(password)
}
override fun onProgress(progressPattern: MutableList<PatternLockView.Dot>?) {
}
override fun onStarted() {
} }
} }

View File

@@ -85,12 +85,12 @@ class SettingsFragment :
DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog") DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
} }
"default_query" -> { "default_query" -> {
DefaultQueryDialog(requireContext()).apply { DefaultQueryDialog().apply {
onPositiveButtonClickListener = { newTags -> onPositiveButtonClickListener = { newTags ->
Preferences["default_query"] = newTags.toString() Preferences["default_query"] = newTags.toString()
summary = newTags.toString() summary = newTags.toString()
} }
}.show() }.show(parentFragmentManager, "Default Query Dialog")
} }
"app_lock" -> { "app_lock" -> {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {

View File

@@ -25,6 +25,7 @@ import android.graphics.drawable.Animatable
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
@@ -110,7 +111,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
) { ) {
when(item) { when(item) {
is TagSuggestion -> { is TagSuggestion -> {
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}" val tag = "${item.n}:${item.s}"
leftIcon?.setImageDrawable( leftIcon?.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(

View File

@@ -2,14 +2,14 @@ package xyz.quaver.pupil.ui.view
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import kotlinx.android.synthetic.main.view_progress_card.view.*
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.ProgressCardViewBinding
class ProgressCard @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, defStyle: Int = R.attr.cardViewStyle) : CardView(context, attr, defStyle) { class ProgressCard @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, defStyle: Int = R.attr.cardViewStyle) : CardView(context, attr, defStyle) {
@@ -29,35 +29,35 @@ class ProgressCard @JvmOverloads constructor(context: Context, attr: AttributeSe
Type.DOWNLOAD -> R.color.material_green_a700 Type.DOWNLOAD -> R.color.material_green_a700
}.let { }.let {
val color = ContextCompat.getColor(context, it) val color = ContextCompat.getColor(context, it)
DrawableCompat.setTint(progressbar.progressDrawable, color) DrawableCompat.setTint(binding.progressbar.progressDrawable, color)
} }
} }
var progress: Int var progress: Int
get() = progressbar?.progress ?: 0 get() = binding.progressbar.progress
set(value) { set(value) {
progressbar?.progress = value binding.progressbar.progress = value
} }
var max: Int var max: Int
get() = progressbar?.max ?: 0 get() = binding.progressbar.max
set(value) { set(value) {
progressbar?.max = value binding.progressbar.max = value
progressbar.visibility = binding.progressbar.visibility =
if (value == 0) if (value == 0)
GONE GONE
else else
VISIBLE VISIBLE
} }
init { val binding = ProgressCardViewBinding.inflate(LayoutInflater.from(context), this)
inflate(context, R.layout.view_progress_card, this)
content.setOnClickListener { init {
binding.content.setOnClickListener {
performClick() performClick()
} }
content.setOnLongClickListener { binding.content.setOnLongClickListener {
performLongClick() performLongClick()
} }
} }
@@ -66,7 +66,7 @@ class ProgressCard @JvmOverloads constructor(context: Context, attr: AttributeSe
if (childCount == 0) if (childCount == 0)
super.addView(child, index, params) super.addView(child, index, params)
else else
content.addView(child, index, params) binding.content.addView(child, index, params)
} }
} }

View File

@@ -137,5 +137,9 @@ operator fun JsonElement.get(index: Int) =
operator fun JsonElement.get(tag: String) = operator fun JsonElement.get(tag: String) =
this.jsonObject[tag] this.jsonObject[tag]
fun JsonElement.getOrNull(tag: String) = kotlin.runCatching {
this.jsonObject.getOrDefault(tag, null)
}.getOrNull()
val JsonElement.content val JsonElement.content
get() = this.jsonPrimitive.contentOrNull get() = this.jsonPrimitive.contentOrNull

View File

@@ -32,7 +32,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -182,7 +181,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
Preferences["update_download_id"] = it Preferences["update_download_id"] = it
} }
} }
setNegativeButton(if (force) android.R.string.cancel else R.string.ignore_update) { _, _ -> setNegativeButton(if (force) android.R.string.cancel else R.string.ignore) { _, _ ->
if (!force) if (!force)
preferences.edit() preferences.edit()
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000) .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
@@ -285,11 +284,11 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it) } folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it) }
}.getOrNull() }.getOrNull()
val galleryID = metadata?.get("reader")?.get("galleryInfo")?.get("id")?.content?.toIntOrNull() val galleryID = metadata?.getOrNull("reader")?.getOrNull("galleryInfo")?.getOrNull("id")?.content?.toIntOrNull()
?: folder.name.toIntOrNull() ?: return@forEachIndexed ?: folder.name.toIntOrNull() ?: return@forEachIndexed
val galleryBlock: GalleryBlock? = kotlin.runCatching { val galleryBlock: GalleryBlock? = kotlin.runCatching {
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) } metadata?.getOrNull("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
getGalleryBlock(galleryID) getGalleryBlock(galleryID)
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
@@ -297,14 +296,14 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
}.getOrNull() }.getOrNull()
val reader: Reader? = kotlin.runCatching { val reader: Reader? = kotlin.runCatching {
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) } metadata?.getOrNull("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
getReader(galleryID) getReader(galleryID)
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
xyz.quaver.hiyobi.getReader(galleryID) xyz.quaver.hiyobi.getReader(galleryID)
}.getOrNull() }.getOrNull()
metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail -> metadata?.getOrNull("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
val file = folder.getChild(".thumbnail").also { val file = folder.getChild(".thumbnail").also {
if (it.exists()) if (it.exists())
it.delete() it.delete()

View File

@@ -28,7 +28,7 @@
tools:ignore="Autofill" tools:ignore="Autofill"
android:inputType="text" android:inputType="text"
android:hint="@string/settings_default_query" android:hint="@string/settings_default_query"
android:id="@+id/default_query_dialog_edittext" android:id="@+id/edittext"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@@ -36,10 +36,10 @@
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>
<LinearLayout <LinearLayout
android:id="@+id/default_query_dialog_language_layout" android:id="@+id/language_layout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_edittext" app:layout_constraintTop_toBottomOf="@id/edittext"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"> app:layout_constraintEnd_toEndOf="parent">
@@ -49,14 +49,14 @@
android:text="@string/default_query_dialog_language"/> android:text="@string/default_query_dialog_language"/>
<Spinner <Spinner
android:id="@+id/default_query_dialog_language_selector" android:id="@+id/language_selector"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/default_query_dialog_BL_layout" android:id="@+id/BL_layout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@@ -64,7 +64,7 @@
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_language_layout" app:layout_constraintTop_toBottomOf="@id/language_layout"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"> app:layout_constraintEnd_toEndOf="parent">
@@ -75,14 +75,14 @@
android:text="@string/default_query_dialog_filter_BL"/> android:text="@string/default_query_dialog_filter_BL"/>
<CheckBox <CheckBox
android:id="@+id/default_query_dialog_BL_checkbox" android:id="@+id/BL_checkbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/default_query_dialog_guro_layout" android:id="@+id/guro_layout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@@ -90,7 +90,7 @@
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_BL_layout" app:layout_constraintTop_toBottomOf="@id/BL_layout"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"> app:layout_constraintEnd_toEndOf="parent">
@@ -101,7 +101,7 @@
android:text="@string/default_query_dialog_filter_guro"/> android:text="@string/default_query_dialog_filter_guro"/>
<CheckBox <CheckBox
android:id="@+id/default_query_dialog_guro_checkbox" android:id="@+id/guro_checkbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@@ -115,7 +115,7 @@
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
app:layout_constraintTop_toBottomOf="@id/default_query_dialog_guro_layout" app:layout_constraintTop_toBottomOf="@id/guro_layout"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"> app:layout_constraintEnd_toEndOf="parent">
@@ -126,7 +126,7 @@
android:text="@string/default_query_dialog_filter_loli"/> android:text="@string/default_query_dialog_filter_loli"/>
<CheckBox <CheckBox
android:id="@+id/default_query_dialog_loli_checkbox" android:id="@+id/loli_checkbox"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@@ -19,14 +19,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/gallery_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/gallery_toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@@ -41,34 +40,34 @@
android:padding="8dp"> android:padding="8dp">
<com.github.piasy.biv.view.BigImageView <com.github.piasy.biv.view.BigImageView
android:id="@+id/gallery_cover" android:id="@+id/cover"
android:layout_width="150dp" android:layout_width="150dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/gallery_title" app:layout_constraintRight_toLeftOf="@id/title"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/gallery_title" android:id="@+id/title"
style="@style/TextAppearance.AppCompat.Headline" style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/gallery_cover" app:layout_constraintLeft_toRightOf="@id/cover"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"/> android:layout_marginStart="8dp"/>
<TextView <TextView
style="@style/TextAppearance.AppCompat.Medium" style="@style/TextAppearance.AppCompat.Medium"
android:id="@+id/gallery_artist" android:id="@+id/artist"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/gallery_title" app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintLeft_toRightOf="@id/gallery_cover" app:layout_constraintLeft_toRightOf="@id/cover"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"/> android:layout_marginStart="8dp"/>
@@ -76,15 +75,15 @@
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/gallery_artist" app:layout_constraintTop_toBottomOf="@id/artist"
app:layout_constraintBottom_toTopOf="@id/gallery_type"/> app:layout_constraintBottom_toTopOf="@id/type"/>
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/gallery_type" android:id="@+id/type"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/gallery_cover" app:layout_constraintLeft_toRightOf="@id/cover"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"/> android:layout_marginStart="8dp"/>
@@ -100,7 +99,7 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout
android:id="@+id/gallery_contents" android:id="@+id/contents"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"/> android:orientation="vertical"/>
@@ -112,7 +111,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<ProgressBar <ProgressBar
android:id="@+id/gallery_progressbar" android:id="@+id/progressbar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
@@ -123,11 +122,11 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/gallery_fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_margin="16dp"
app:layout_anchor="@id/gallery_toolbar" app:layout_anchor="@id/toolbar"
app:layout_anchorGravity="bottom|end"/> app:layout_anchorGravity="bottom|end"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -25,14 +25,14 @@
android:padding="8dp"> android:padding="8dp">
<TextView <TextView
android:id="@+id/gallery_details" android:id="@+id/type"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/TextAppearance.MaterialComponents.Body1" style="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/colorAccent"/> android:textColor="@color/colorAccent"/>
<LinearLayout <LinearLayout
android:id="@+id/gallery_details_contents" android:id="@+id/contents"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"/> android:orientation="vertical"/>

View File

@@ -25,7 +25,7 @@
android:layout_margin="8dp"> android:layout_margin="8dp">
<com.tbuonomo.viewpagerdotsindicator.DotsIndicator <com.tbuonomo.viewpagerdotsindicator.DotsIndicator
android:id="@+id/gallery_dotindicator" android:id="@+id/dotindicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"

View File

@@ -26,13 +26,13 @@
<TextView <TextView
style="@style/TextAppearance.MaterialComponents.Body2" style="@style/TextAppearance.MaterialComponents.Body2"
android:id="@+id/gallery_details_type" android:id="@+id/type"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="8dp"/> android:paddingBottom="8dp"/>
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/gallery_details_tags" android:id="@+id/tags"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:chipSpacingVertical="4dp"/> app:chipSpacingVertical="4dp"/>

View File

@@ -41,7 +41,7 @@
app:layout_constraintBottom_toTopOf="@id/lock_button_layout"> app:layout_constraintBottom_toTopOf="@id/lock_button_layout">
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/lock_fingerprint" android:id="@+id/fingerprint_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/fingerprint" app:srcCompat="@drawable/fingerprint"
@@ -64,7 +64,7 @@
android:gravity="center"> android:gravity="center">
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/lock_pattern" android:id="@+id/pattern_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:tint="@null" app:tint="@null"
@@ -73,7 +73,7 @@
app:fabSize="mini"/> app:fabSize="mini"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/lock_pin" android:id="@+id/pin_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:tint="@null" app:tint="@null"
@@ -84,7 +84,7 @@
app:fabSize="mini"/> app:fabSize="mini"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/lock_password" android:id="@+id/password_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:tint="@null" app:tint="@null"

View File

@@ -21,17 +21,18 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_drawer_layout" android:id="@+id/drawer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:openDrawer="start"> tools:openDrawer="start">
<include layout="@layout/activity_main_content" <include android:id="@+id/contents"
layout="@layout/main_activity_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"/>
<com.google.android.material.navigation.NavigationView <com.google.android.material.navigation.NavigationView
android:id="@+id/main_nav_view" android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"

View File

@@ -25,7 +25,7 @@
tools:context=".ui.MainActivity"> tools:context=".ui.MainActivity">
<xyz.quaver.pupil.ui.view.MainView <xyz.quaver.pupil.ui.view.MainView
android:id="@+id/main_view" android:id="@+id/view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -43,7 +43,7 @@
app:popupDrawable="@android:color/transparent"> app:popupDrawable="@android:color/transparent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="64dp" android:paddingTop="64dp"
@@ -56,14 +56,14 @@
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
style="?android:attr/progressBarStyle" style="?android:attr/progressBarStyle"
android:id="@+id/main_progressbar" android:id="@+id/progressbar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:indeterminate="true"/> android:indeterminate="true"/>
<TextView <TextView
android:id="@+id/main_noresult" android:id="@+id/noresult"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@@ -72,7 +72,7 @@
android:visibility="invisible"/> android:visibility="invisible"/>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
@@ -80,28 +80,28 @@
app:menu_colorNormal="@color/colorAccent"> app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_cancel" android:id="@+id/cancel_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_label="@string/main_fab_cancel" app:fab_label="@string/main_fab_cancel"
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_jump" android:id="@+id/jump_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_label="@string/main_jump_title" app:fab_label="@string/main_jump_title"
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_random" android:id="@+id/random_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_label="@string/main_fab_random" app:fab_label="@string/main_fab_random"
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_id" android:id="@+id/id_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:fab_label="@string/main_open_gallery_by_id" app:fab_label="@string/main_open_gallery_by_id"
@@ -110,7 +110,7 @@
</com.github.clans.fab.FloatingActionMenu> </com.github.clans.fab.FloatingActionMenu>
<xyz.quaver.pupil.ui.view.FloatingSearchView <xyz.quaver.pupil.ui.view.FloatingSearchView
android:id="@+id/main_searchview" android:id="@+id/searchview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:searchBarMarginLeft="6dp" app:searchBarMarginLeft="6dp"

View File

@@ -25,7 +25,7 @@
<TextView <TextView
style="?android:textAppearanceLarge" style="?android:textAppearanceLarge"
android:id="@+id/dialog_title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/reader_go_to_page" android:text="@string/reader_go_to_page"
@@ -33,20 +33,20 @@
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"/>
<NumberPicker <NumberPicker
android:id="@+id/dialog_number_picker" android:id="@+id/number_picker"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/dialog_title" app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>
<Button <Button
android:id="@+id/dialog_ok" android:id="@+id/ok_button"
style="?borderlessButtonStyle" style="?borderlessButtonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@android:string/ok" android:text="@android:string/ok"
app:layout_constraintTop_toBottomOf="@id/dialog_number_picker" app:layout_constraintTop_toBottomOf="@id/number_picker"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>

View File

@@ -25,7 +25,7 @@
tools:context=".ui.fragment.PatternLockFragment"> tools:context=".ui.fragment.PatternLockFragment">
<com.andrognito.patternlockview.PatternLockView <com.andrognito.patternlockview.PatternLockView
android:id="@+id/lock_pattern_view" android:id="@+id/pattern_lock_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"

View File

@@ -24,7 +24,7 @@
android:padding="16dp"> android:padding="16dp">
<TextView <TextView
android:id="@+id/proxy_title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="16dp" android:paddingBottom="16dp"
@@ -35,46 +35,46 @@
app:layout_constraintLeft_toLeftOf="parent"/> app:layout_constraintLeft_toLeftOf="parent"/>
<TextView <TextView
android:id="@+id/proxy_type_text" android:id="@+id/type_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_title" app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:text="@string/proxy_dialog_type" android:text="@string/proxy_dialog_type"
android:textAppearance="?android:attr/listSeparatorTextViewStyle"/> android:textAppearance="?android:attr/listSeparatorTextViewStyle"/>
<Spinner <Spinner
android:id="@+id/proxy_type_selector" android:id="@+id/type_selector"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_type_text"/> app:layout_constraintTop_toBottomOf="@id/type_text"/>
<TextView <TextView
android:id="@+id/proxy_server_text" android:id="@+id/server_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_type_selector" app:layout_constraintTop_toBottomOf="@id/type_selector"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:text="@string/proxy_dialog_server" android:text="@string/proxy_dialog_server"
android:textAppearance="?android:attr/listSeparatorTextViewStyle"/> android:textAppearance="?android:attr/listSeparatorTextViewStyle"/>
<LinearLayout <LinearLayout
android:id="@+id/proxy_address_layout" android:id="@+id/address_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_server_text"> app:layout_constraintTop_toBottomOf="@id/server_text">
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/proxy_addr" android:id="@+id/addr"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="2" android:layout_weight="2"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/proxy_dialog_addr_hint"/> android:hint="@string/proxy_dialog_addr_hint"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/proxy_port" android:id="@+id/port"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -83,39 +83,39 @@
</LinearLayout> </LinearLayout>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/proxy_username" android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_address_layout" app:layout_constraintTop_toBottomOf="@id/address_layout"
android:hint="@string/proxy_dialog_username_hint" android:hint="@string/proxy_dialog_username_hint"
android:enabled="false"/> android:enabled="false"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/proxy_password" android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/proxy_username" app:layout_constraintTop_toBottomOf="@id/username"
android:hint="@string/proxy_dialog_password_hint" android:hint="@string/proxy_dialog_password_hint"
android:enabled="false"/> android:enabled="false"/>
<Button <Button
android:id="@+id/proxy_cancel" android:id="@+id/cancel_button"
style="?borderlessButtonStyle" style="?borderlessButtonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@android:string/cancel" android:text="@android:string/cancel"
app:layout_constraintTop_toBottomOf="@id/proxy_password" app:layout_constraintTop_toBottomOf="@id/password"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/proxy_ok" app:layout_constraintEnd_toStartOf="@id/ok_button"
app:layout_constraintRight_toLeftOf="@id/proxy_ok"/> app:layout_constraintRight_toLeftOf="@id/ok_button"/>
<Button <Button
android:id="@+id/proxy_ok" android:id="@+id/ok_button"
style="?borderlessButtonStyle" style="?borderlessButtonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@android:string/ok" android:text="@android:string/ok"
app:layout_constraintTop_toBottomOf="@id/proxy_password" app:layout_constraintTop_toBottomOf="@id/password"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"/> app:layout_constraintRight_toRightOf="parent"/>

View File

@@ -18,7 +18,6 @@
--> -->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/reader_layout"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -40,7 +39,7 @@
app:popupDrawable="@android:color/transparent"> app:popupDrawable="@android:color/transparent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/reader_recyclerview" android:id="@+id/recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
@@ -56,13 +55,13 @@
android:layout_margin="8dp"/> android:layout_margin="8dp"/>
<ProgressBar <ProgressBar
android:id="@+id/reader_download_progressbar" android:id="@+id/download_progressbar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="4dp"/> android:layout_height="4dp"/>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/reader_fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
@@ -70,7 +69,7 @@
app:menu_colorNormal="@color/colorAccent"> app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/reader_fab_download" android:id="@+id/download_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_download" app:srcCompat="@drawable/ic_download"
@@ -78,7 +77,7 @@
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/reader_fab_retry" android:id="@+id/retry_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/refresh" app:srcCompat="@drawable/refresh"
@@ -86,7 +85,7 @@
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/reader_fab_auto" android:id="@+id/auto_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/eye_white" app:srcCompat="@drawable/eye_white"
@@ -94,7 +93,7 @@
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/reader_fab_fullscreen" android:id="@+id/fullscreen_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_fullscreen" app:srcCompat="@drawable/ic_fullscreen"

View File

@@ -73,7 +73,7 @@
<string name="main_menu_sort">ソート</string> <string name="main_menu_sort">ソート</string>
<string name="main_menu_sort_newest">投稿日時順</string> <string name="main_menu_sort_newest">投稿日時順</string>
<string name="main_menu_sort_popular">人気順</string> <string name="main_menu_sort_popular">人気順</string>
<string name="ignore_update">無視</string> <string name="ignore">無視</string>
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string> <string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string>
<string name="settings_dark_mode_title">ダークモード</string> <string name="settings_dark_mode_title">ダークモード</string>
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string> <string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
@@ -155,4 +155,5 @@
<string name="settings_tag_translation">タグ言語</string> <string name="settings_tag_translation">タグ言語</string>
<string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string> <string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string>
<string name="settings_concurrent_download">並列ダウンロード</string> <string name="settings_concurrent_download">並列ダウンロード</string>
<string name="unaccessible_download_folder">アンドロイド11以上では外部からのアプリ内部空間接近が不可能です。ダウンロードフォルダを変更しますか</string>
</resources> </resources>

View File

@@ -71,7 +71,7 @@
<string name="main_menu_sort">정렬</string> <string name="main_menu_sort">정렬</string>
<string name="main_menu_sort_popular">인기순</string> <string name="main_menu_sort_popular">인기순</string>
<string name="main_menu_sort_newest">시간순</string> <string name="main_menu_sort_newest">시간순</string>
<string name="ignore_update">무시</string> <string name="ignore">무시</string>
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string> <string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
<string name="settings_dark_mode_title">다크 모드</string> <string name="settings_dark_mode_title">다크 모드</string>
<string name="settings_dark_mode_summary">딥 다크한 모오드</string> <string name="settings_dark_mode_summary">딥 다크한 모오드</string>
@@ -155,4 +155,5 @@
<string name="settings_tag_translation">태그 언어</string> <string name="settings_tag_translation">태그 언어</string>
<string name="settings_tag_translation_message">Github에서 번역에 참여하세요</string> <string name="settings_tag_translation_message">Github에서 번역에 참여하세요</string>
<string name="settings_concurrent_download">병렬 다운로드</string> <string name="settings_concurrent_download">병렬 다운로드</string>
<string name="unaccessible_download_folder">안드로이드 11 이상에서는 외부에서 현재 다운로드 폴더에 접근할 수 없습니다. 변경하시겠습니까?</string>
</resources> </resources>

View File

@@ -28,7 +28,7 @@
<string name="warning">Warning</string> <string name="warning">Warning</string>
<string name="error">Error</string> <string name="error">Error</string>
<string name="ignore_update">Ignore</string> <string name="ignore">Ignore</string>
<string name="copied_to_clipboard">Copied to clipboard</string> <string name="copied_to_clipboard">Copied to clipboard</string>
@@ -47,6 +47,8 @@
<string name="main_no_result">No result</string> <string name="main_no_result">No result</string>
<string name="unaccessible_download_folder">From Android 11 and above, current Download folder cannot be accessed by outside apps. Would you like to change the download folder?</string>
<string name="main_drawer_home">Home</string> <string name="main_drawer_home">Home</string>
<string name="main_drawer_history">History</string> <string name="main_drawer_history">History</string>
<string name="main_drawer_downloads">Downloads</string> <string name="main_drawer_downloads">Downloads</string>