Add paging
This commit is contained in:
tom5079
2019-06-01 21:42:14 +09:00
parent f326c69902
commit 530da98ec6
21 changed files with 605 additions and 204 deletions

2
.idea/misc.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" /> <output url="file://$PROJECT_DIR$/classes" />
</component> </component>
</project> </project>

View File

@@ -9,8 +9,8 @@ android {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode 7 versionCode 8
versionName "2.1" versionName "2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@@ -6,16 +6,17 @@ import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.text.* import android.text.*
import android.text.style.AlignmentSpan import android.text.style.AlignmentSpan
import android.view.LayoutInflater import android.util.Log
import android.view.View import android.view.*
import android.view.WindowManager import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.arlib.floatingsearchview.FloatingSearchView import com.arlib.floatingsearchview.FloatingSearchView
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
import com.arlib.floatingsearchview.util.view.SearchInputView import com.arlib.floatingsearchview.util.view.SearchInputView
@@ -39,6 +40,7 @@ import java.net.URL
import java.util.* import java.util.*
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.roundToInt
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@@ -56,7 +58,9 @@ class MainActivity : AppCompatActivity() {
private val SETTINGS = 45162 private val SETTINGS = 45162
private var galleryIDs: Deferred<List<Int>>? = null private var galleryIDs: Deferred<List<Int>>? = null
private var totalItems = 0
private var loadingJob: Job? = null private var loadingJob: Job? = null
private var currentPage = 0
private lateinit var histories: Histories private lateinit var histories: Histories
private lateinit var downloads: Histories private lateinit var downloads: Histories
@@ -64,16 +68,25 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val preference = PreferenceManager.getDefaultSharedPreferences(this)
if (Locale.getDefault().language == "ko") {
if (!preference.getBoolean("https_block_alert", false)) {
android.app.AlertDialog.Builder(this).apply {
setTitle(R.string.https_block_alert_title)
setMessage(R.string.https_block_alert)
setPositiveButton(android.R.string.ok) { _, _ -> }
}.show()
preference.edit().putBoolean("https_block_alert", true).apply()
}
}
with(application as Pupil) { with(application as Pupil) {
this@MainActivity.histories = histories this@MainActivity.histories = histories
this@MainActivity.downloads = downloads this@MainActivity.downloads = downloads
} }
window.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
checkUpdate() checkUpdate()
@@ -109,6 +122,44 @@ class MainActivity : AppCompatActivity() {
super.onResume() super.onResume()
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
val preference = PreferenceManager.getDefaultSharedPreferences(this)
val perPage = preference.getString("per_page", "25")!!.toInt()
val maxPage = Math.ceil(totalItems / perPage.toDouble()).roundToInt()
return when(keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (currentPage < maxPage) {
runOnUiThread {
currentPage++
cancelFetch()
clearGalleries()
fetchGalleries(query)
loadBlocks()
}
}
true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
if (currentPage > 0) {
runOnUiThread {
currentPage--
cancelFetch()
clearGalleries()
fetchGalleries(query)
loadBlocks()
}
}
true
}
else -> super.onKeyDown(keyCode, event)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when(requestCode) { when(requestCode) {
@@ -188,31 +239,16 @@ class MainActivity : AppCompatActivity() {
} }
private fun initView() { private fun initView() {
var prevP1 = 0
main_appbar_layout.addOnOffsetChangedListener( main_appbar_layout.addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { _, p1 -> AppBarLayout.OnOffsetChangedListener { _, p1 ->
main_searchview.translationY = p1.toFloat() main_searchview.translationY = p1.toFloat()
main_recyclerview.translationY = p1.toFloat() main_recyclerview.scrollBy(0, prevP1 - p1)
prevP1 = p1
} }
) )
//SwipeRefreshLayout
with(main_swipe_layout) {
setProgressViewOffset(
false,
resources.getDimensionPixelSize(R.dimen.progress_view_start),
resources.getDimensionPixelSize(R.dimen.progress_view_offset)
)
setOnRefreshListener {
post {
cancelFetch()
clearGalleries()
fetchGalleries(query)
loadBlocks()
}
}
}
//NavigationView //NavigationView
main_nav_view.setNavigationItemSelectedListener { main_nav_view.setNavigationItemSelectedListener {
runOnUiThread { runOnUiThread {
@@ -276,7 +312,7 @@ class MainActivity : AppCompatActivity() {
with(main_recyclerview) { with(main_recyclerview) {
adapter = GalleryBlockAdapter(galleries).apply { adapter = GalleryBlockAdapter(galleries).apply {
onChipClickedHandler.add { onChipClickedHandler.add {
post { runOnUiThread {
query = it.toQuery() query = it.toQuery()
this@MainActivity.findViewById<SearchInputView>(R.id.search_bar_text) this@MainActivity.findViewById<SearchInputView>(R.id.search_bar_text)
.setText(query, TextView.BufferType.EDITABLE) .setText(query, TextView.BufferType.EDITABLE)
@@ -288,21 +324,12 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
addOnScrollListener(
object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
if (loadingJob?.isActive != true)
if (layoutManager.findLastCompletelyVisibleItemPosition() == galleries.size)
loadBlocks()
}
}
)
ItemClickSupport.addTo(this) ItemClickSupport.addTo(this)
.setOnItemClickListener { _, position, _ -> .setOnItemClickListener { _, position, v ->
if (v !is CardView)
return@setOnItemClickListener
val intent = Intent(this@MainActivity, ReaderActivity::class.java) val intent = Intent(this@MainActivity, ReaderActivity::class.java)
val gallery = galleries[position].first val gallery = galleries[position].first
intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery))
@@ -311,7 +338,11 @@ class MainActivity : AppCompatActivity() {
startActivity(intent) startActivity(intent)
histories.add(gallery.id) histories.add(gallery.id)
}.setOnItemLongClickListener { recyclerView, position, _ -> }.setOnItemLongClickListener { recyclerView, position, v ->
if (v !is CardView)
return@setOnItemLongClickListener true
val galleryBlock = galleries[position].first val galleryBlock = galleries[position].first
val view = LayoutInflater.from(this@MainActivity) val view = LayoutInflater.from(this@MainActivity)
.inflate(R.layout.dialog_galleryblock, recyclerView, false) .inflate(R.layout.dialog_galleryblock, recyclerView, false)
@@ -358,6 +389,210 @@ class MainActivity : AppCompatActivity() {
true true
} }
var origin = 0f
var target = -1
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val perPage = preferences.getString("per_page", "25")!!.toInt()
setOnTouchListener { _, event ->
when(event.action) {
MotionEvent.ACTION_UP -> {
origin = 0f
with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(showPrev) {
showPrev = false
val prev = main_recyclerview.layoutManager?.getChildAt(0)
if (prev is LinearLayout) {
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
prev.layoutParams.height = 1
icon.layoutParams.height = 1
icon.rotation = 180f
}
prev?.requestLayout()
notifyItemRemoved(0)
}
if(showNext) {
showNext = false
val next = main_recyclerview.layoutManager?.let {
getChildAt(childCount-1)
}
if (next is LinearLayout) {
val icon = next.findViewById<ImageView>(R.id.icon_next)
next.layoutParams.height = 1
icon.layoutParams.height = 1
icon.rotation = 0f
}
next?.requestLayout()
notifyItemRemoved(itemCount)
}
}
if (target != -1) {
currentPage = target
runOnUiThread {
cancelFetch()
clearGalleries()
fetchGalleries(query)
loadBlocks()
}
target = -1
}
}
MotionEvent.ACTION_DOWN -> origin = event.y
MotionEvent.ACTION_MOVE -> {
if (origin == 0f)
origin = event.y
val dist = event.y - origin
when {
!canScrollVertically(-1) -> {
//TOP
//Scrolling UP
if (dist > 0 && currentPage != 0) {
with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(!showPrev) {
showPrev = true
notifyItemInserted(0)
}
}
val prev = main_recyclerview.layoutManager?.getChildAt(0)
if (prev is LinearLayout) {
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
val text = prev.findViewById<TextView>(R.id.text_prev).apply {
text = getString(R.string.main_move, currentPage)
}
if (dist < 360) {
prev.layoutParams.height = (dist/2).roundToInt()
icon.layoutParams.height = (dist/2).roundToInt()
icon.rotation = dist+180
text.layoutParams.width = dist.roundToInt()
target = -1
}
else {
prev.layoutParams.height = 180
icon.layoutParams.height = 180
icon.rotation = 180f
text.layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
target = currentPage-1
}
}
prev?.requestLayout()
return@setOnTouchListener true
} else {
with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(showPrev) {
showPrev = false
val prev = main_recyclerview.layoutManager?.getChildAt(0)
if (prev is LinearLayout) {
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
prev.layoutParams.height = 1
icon.layoutParams.height = 1
icon.rotation = 180f
}
prev?.requestLayout()
notifyItemRemoved(0)
}
}
}
}
!canScrollVertically(1) -> {
//BOTTOM
//Scrolling DOWN
if (dist < 0 && currentPage != Math.ceil(totalItems.toDouble()/perPage).roundToInt()-1) {
with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(!showNext) {
showNext = true
notifyItemInserted(itemCount-1)
}
}
val next = main_recyclerview.layoutManager?.let {
getChildAt(childCount-1)
}
val absDist = Math.abs(dist)
if (next is LinearLayout) {
val icon = next.findViewById<ImageView>(R.id.icon_next)
val text = next.findViewById<TextView>(R.id.text_next).apply {
text = getString(R.string.main_move, currentPage+2)
}
if (absDist < 360) {
next.layoutParams.height = (absDist/2).roundToInt()
icon.layoutParams.height = (absDist/2).roundToInt()
icon.rotation = -absDist
text.layoutParams.width = absDist.roundToInt()
target = -1
}
else {
next.layoutParams.height = 180
icon.layoutParams.height = 180
icon.rotation = 0f
text.layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
target = currentPage+1
}
}
next?.requestLayout()
return@setOnTouchListener true
} else {
with(main_recyclerview.adapter as GalleryBlockAdapter) {
if(showNext) {
showNext = false
val next = main_recyclerview.layoutManager?.let {
getChildAt(childCount-1)
}
if (next is LinearLayout) {
Log.d("Pupil", "hmm...")
val icon = next.findViewById<ImageView>(R.id.icon_next)
next.layoutParams.height = 1
icon.layoutParams.height = 1
icon.rotation = 180f
}
next?.requestLayout()
notifyItemRemoved(itemCount)
}
}
}
}
}
}
}
false
}
} }
} }
@@ -386,7 +621,33 @@ class MainActivity : AppCompatActivity() {
setOnMenuItemClickListener { setOnMenuItemClickListener {
when(it.itemId) { when(it.itemId) {
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), SETTINGS) R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), SETTINGS)
R.id.main_menu_search -> setSearchFocused(true) R.id.main_menu_page_indicator -> {
val preference = PreferenceManager.getDefaultSharedPreferences(context)
val perPage = preference.getString("per_page", "25")!!.toInt()
val editText = EditText(context)
AlertDialog.Builder(context).apply {
title = getString(R.string.reader_go_to_page)
setView(editText)
setTitle(R.string.main_jump_title)
setMessage(getString(
R.string.main_jump_message,
currentPage+1,
Math.ceil(totalItems / perPage.toDouble()).roundToInt()
))
setPositiveButton(android.R.string.ok) { _, _ ->
currentPage = (editText.text.toString().toIntOrNull() ?: return@setPositiveButton)-1
runOnUiThread {
cancelFetch()
clearGalleries()
fetchGalleries(query)
loadBlocks()
}
}
}.show()
}
} }
} }
@@ -471,7 +732,7 @@ class MainActivity : AppCompatActivity() {
if (query != this@MainActivity.query) { if (query != this@MainActivity.query) {
this@MainActivity.query = query this@MainActivity.query = query
CoroutineScope(Dispatchers.Main).launch { runOnUiThread {
cancelFetch() cancelFetch()
clearGalleries() clearGalleries()
fetchGalleries(query) fetchGalleries(query)
@@ -502,12 +763,12 @@ class MainActivity : AppCompatActivity() {
this.notifyDataSetChanged() this.notifyDataSetChanged()
} }
main_appbar_layout.setExpanded(true)
main_noresult.visibility = View.INVISIBLE main_noresult.visibility = View.INVISIBLE
main_progressbar.show() main_progressbar.show()
main_swipe_layout.isRefreshing = false
} }
private fun fetchGalleries(query: String, from: Int = 0) { private fun fetchGalleries(query: String) {
val preference = PreferenceManager.getDefaultSharedPreferences(this) val preference = PreferenceManager.getDefaultSharedPreferences(this)
val perPage = preference.getString("per_page", "25")?.toInt() ?: 25 val perPage = preference.getString("per_page", "25")?.toInt() ?: 25
val defaultQuery = preference.getString("default_query", "")!! val defaultQuery = preference.getString("default_query", "")!!
@@ -521,27 +782,42 @@ class MainActivity : AppCompatActivity() {
when(mode) { when(mode) {
Mode.SEARCH -> { Mode.SEARCH -> {
when { when {
query.isEmpty() and defaultQuery.isEmpty() -> query.isEmpty() and defaultQuery.isEmpty() -> {
fetchNozomi(start = from, count = perPage) fetchNozomi(start = currentPage*perPage, count = perPage).let {
else -> totalItems = it.second
doSearch("$defaultQuery $query") it.first
}
}
else -> doSearch("$defaultQuery $query").apply {
totalItems = size
}
} }
} }
Mode.HISTORY -> { Mode.HISTORY -> {
when { when {
query.isEmpty() -> histories.toList() query.isEmpty() -> {
histories.toList().apply {
totalItems = size
}
}
else -> { else -> {
val result = doSearch(query).sorted() val result = doSearch(query).sorted()
histories.filter { result.binarySearch(it) >= 0 } histories.filter { result.binarySearch(it) >= 0 }.apply {
totalItems = size
}
} }
} }
} }
Mode.DOWNLOAD -> { Mode.DOWNLOAD -> {
when { when {
query.isEmpty() -> downloads.toList() query.isEmpty() -> downloads.toList().apply {
totalItems = size
}
else -> { else -> {
val result = doSearch(query).sorted() val result = doSearch(query).sorted()
downloads.filter { result.binarySearch(it) >= 0 } downloads.filter { result.binarySearch(it) >= 0 }.apply {
totalItems = size
}
} }
} }
} }
@@ -566,18 +842,11 @@ class MainActivity : AppCompatActivity() {
return@launch return@launch
} }
if (query.isEmpty() and defaultQuery.isEmpty())
fetchGalleries("", galleries.size+perPage)
else
with(main_recyclerview.adapter as GalleryBlockAdapter) {
noMore = galleries.size + perPage >= galleryIDs.size
}
when { when {
query.isEmpty() and defaultQuery.isEmpty() -> query.isEmpty() and defaultQuery.isEmpty() and (mode == Mode.SEARCH) ->
galleryIDs galleryIDs
else -> else ->
galleryIDs.slice(galleries.size until Math.min(galleries.size+perPage, galleryIDs.size)) galleryIDs.slice(currentPage*perPage until Math.min(currentPage*perPage+perPage, galleryIDs.size))
}.chunked(5).let { chunks -> }.chunked(5).let { chunks ->
for (chunk in chunks) for (chunk in chunks)
chunk.map { chunk.map {
@@ -635,8 +904,7 @@ class MainActivity : AppCompatActivity() {
if (galleryBlock != null) { if (galleryBlock != null) {
galleries.add(galleryBlock) galleries.add(galleryBlock)
main_recyclerview.adapter!!.notifyItemInserted(galleries.size - 1)
main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1)
} }
} }
} }

View File

@@ -12,12 +12,14 @@ import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.*
import kotlinx.android.synthetic.main.activity_reader.view.* import kotlinx.android.synthetic.main.activity_reader.view.*
import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.android.synthetic.main.dialog_numberpicker.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
import kotlinx.io.IOException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
@@ -89,7 +91,7 @@ class ReaderActivity : AppCompatActivity() {
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, findViewById(android.R.id.content), false) val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
with(view.reader_dialog_number_picker) { with(view.dialog_number_picker) {
minValue=1 minValue=1
maxValue=gallerySize maxValue=gallerySize
value=currentPage value=currentPage
@@ -97,8 +99,8 @@ class ReaderActivity : AppCompatActivity() {
val dialog = AlertDialog.Builder(this).apply { val dialog = AlertDialog.Builder(this).apply {
setView(view) setView(view)
}.create() }.create()
view.reader_dialog_ok.setOnClickListener { view.dialog_ok.setOnClickListener {
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(view.reader_dialog_number_picker.value-1, 0) (reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(view.dialog_number_picker.value-1, 0)
dialog.dismiss() dialog.dismiss()
} }
@@ -135,7 +137,13 @@ class ReaderActivity : AppCompatActivity() {
var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id) var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id)
if (d == null) { if (d == null) {
d = GalleryDownloader(this, galleryBlock) try {
d = GalleryDownloader(this, galleryBlock)
} catch (e: IOException) {
Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show()
finish()
return
}
} }
downloader = d.apply { downloader = d.apply {
@@ -170,6 +178,8 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
onErrorHandler = { onErrorHandler = {
if (it is IOException)
Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show()
downloader.download = false downloader.download = false
} }
onCompleteHandler = { onCompleteHandler = {

View File

@@ -1,12 +1,13 @@
package xyz.quaver.pupil.adapters package xyz.quaver.pupil.adapters
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.util.SparseArray import android.util.Log
import android.util.SparseBooleanArray import android.util.SparseBooleanArray
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.LinearLayout
import android.widget.RelativeLayout
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -27,66 +28,28 @@ import xyz.quaver.pupil.types.Tag
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferred<String>>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferred<String>>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private enum class ViewType { enum class ViewType {
VIEW_ITEM, NEXT,
VIEW_PROG GALLERY,
PREV
} }
private fun String.wordCapitalize() : String { inner class GalleryViewHolder(private val view: CardView) : RecyclerView.ViewHolder(view) {
val result = ArrayList<String>() fun bind(item: Pair<GalleryBlock, Deferred<String>>) {
with(view) {
for (word in this.split(" "))
result.add(word.capitalize())
return result.joinToString(" ")
}
var noMore = false
private val refreshTasks = SparseArray<TimerTask>()
val completeFlag = SparseBooleanArray()
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
class ViewHolder(val view: CardView, var galleryID: Int? = null) : RecyclerView.ViewHolder(view)
class ProgressViewHolder(val view: LinearLayout) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when(viewType) {
ViewType.VIEW_ITEM.ordinal -> {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_galleryblock, parent, false
) as CardView
return ViewHolder(view)
}
ViewType.VIEW_PROG.ordinal -> {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.item_progressbar, parent, false
) as LinearLayout
return ProgressViewHolder(view)
}
}
throw Exception("Unexpected ViewType")
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolder) {
with(holder.view) {
val resources = 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() }.toMap()
val (gallery, thumbnail) = galleries[position]
holder.galleryID = gallery.id val (gallery: GalleryBlock, thumbnail: Deferred<String>) = item
val artists = gallery.artists val artists = gallery.artists
val series = gallery.series val series = gallery.series
@@ -99,7 +62,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
val bitmap = BitmapFactory.decodeFile(thumbnail.await()) val bitmap = BitmapFactory.decodeFile(thumbnail.await())
CoroutineScope(Dispatchers.Main).launch { post {
galleryblock_thumbnail.setImageBitmap(bitmap) galleryblock_thumbnail.setImageBitmap(bitmap)
} }
} }
@@ -122,10 +85,10 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
galleryblock_progressbar.visibility = View.GONE galleryblock_progressbar.visibility = View.GONE
} }
if (refreshTasks.get(gallery.id) == null) { if (refreshTasks[this@GalleryViewHolder] == null) {
val refresh = Timer(false).schedule(0, 1000) { val refresh = Timer(false).schedule(0, 1000) {
post { post {
with(galleryblock_progressbar) { with(view.galleryblock_progressbar) {
progress = imageCache.list()?.size ?: 0 progress = imageCache.list()?.size ?: 0
if (!readerCache.exists()) { if (!readerCache.exists()) {
@@ -133,7 +96,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
max = 0 max = 0
progress = 0 progress = 0
holder.view.galleryblock_progress_complete.visibility = View.INVISIBLE view.galleryblock_progress_complete.visibility = View.INVISIBLE
} else { } else {
if (visibility == View.GONE) { if (visibility == View.GONE) {
val reader = Json(JsonConfiguration.Stable) val reader = Json(JsonConfiguration.Stable)
@@ -144,24 +107,21 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
if (progress == max) { if (progress == max) {
if (completeFlag.get(gallery.id, false)) { if (completeFlag.get(gallery.id, false)) {
with(holder.view.galleryblock_progress_complete) { with(view.galleryblock_progress_complete) {
setImageResource(R.drawable.ic_progressbar) setImageResource(R.drawable.ic_progressbar)
visibility = View.VISIBLE visibility = View.VISIBLE
} }
} else { } else {
val drawable = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_progressbar_complete) val drawable = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_progressbar_complete)
with(holder.view.galleryblock_progress_complete) { with(view.galleryblock_progress_complete) {
setImageDrawable(drawable) setImageDrawable(drawable)
visibility = View.VISIBLE visibility = View.VISIBLE
} }
drawable?.start() drawable?.start()
completeFlag.put(gallery.id, true) completeFlag.put(gallery.id, true)
} }
} else { } else
with(holder.view.galleryblock_progress_complete) { view.galleryblock_progress_complete.visibility = View.INVISIBLE
visibility = View.INVISIBLE
}
}
null null
} }
@@ -169,7 +129,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
} }
} }
refreshTasks.put(gallery.id, refresh) refreshTasks[this@GalleryViewHolder] = refresh
} }
galleryblock_title.text = gallery.title galleryblock_title.text = gallery.title
@@ -205,7 +165,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
val tag = Tag.parse(it) val tag = Tag.parse(it)
val chip = LayoutInflater.from(context) val chip = LayoutInflater.from(context)
.inflate(R.layout.tag_chip, holder.view, false) as Chip .inflate(R.layout.tag_chip, this, false) as Chip
val icon = when(tag.area) { val icon = when(tag.area) {
"male" -> { "male" -> {
@@ -232,32 +192,85 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
} }
} }
} }
if (holder is ProgressViewHolder) { }
holder.view.visibility = when(noMore) { class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
true -> View.GONE class PrevViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
false -> View.VISIBLE
class ViewHolderFactory {
companion object {
fun getLayoutID(type: Int): Int {
return when(ViewType.values()[type]) {
ViewType.NEXT -> R.layout.item_next
ViewType.PREV -> R.layout.item_prev
ViewType.GALLERY -> R.layout.item_galleryblock
}
} }
} }
} }
private fun String.wordCapitalize() : String {
val result = ArrayList<String>()
for (word in this.split(" "))
result.add(word.capitalize())
return result.joinToString(" ")
}
private val refreshTasks = HashMap<GalleryViewHolder, TimerTask>()
val completeFlag = SparseBooleanArray()
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
var showNext = false
var showPrev = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
fun getViewHolder(type: Int, view: View): RecyclerView.ViewHolder {
return when(ViewType.values()[type]) {
ViewType.NEXT -> NextViewHolder(view as LinearLayout)
ViewType.PREV -> PrevViewHolder(view as LinearLayout)
ViewType.GALLERY -> GalleryViewHolder(view as CardView)
}
}
return getViewHolder(
viewType,
LayoutInflater.from(parent.context).inflate(
ViewHolderFactory.getLayoutID(viewType),
parent,
false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is GalleryViewHolder)
holder.bind(galleries[position-(if (showPrev) 1 else 0)])
}
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
super.onViewDetachedFromWindow(holder) super.onViewDetachedFromWindow(holder)
if (holder is ViewHolder) { if (holder is GalleryViewHolder) {
val galleryID = holder.galleryID ?: return val task = refreshTasks[holder] ?: return
val task = refreshTasks.get(galleryID) ?: return
refreshTasks.remove(galleryID)
task.cancel() task.cancel()
refreshTasks.remove(holder)
} }
} }
override fun getItemCount() = if (galleries.isEmpty()) 0 else galleries.size+1 override fun getItemCount() =
(if (galleries.isEmpty()) 0 else galleries.size)+
(if (showNext) 1 else 0)+
(if (showPrev) 1 else 0)
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when { return when {
galleries.getOrNull(position) == null -> ViewType.VIEW_PROG.ordinal showPrev && position == 0 -> ViewType.PREV
else -> ViewType.VIEW_ITEM.ordinal showNext && position == galleries.size+(if (showPrev) 1 else 0) -> ViewType.NEXT
} else -> ViewType.GALLERY
}.ordinal
} }
} }

View File

@@ -55,7 +55,7 @@ class GalleryDownloader(
var onReaderLoadedHandler: ((Reader) -> Unit)? = null var onReaderLoadedHandler: ((Reader) -> Unit)? = null
var onProgressHandler: ((Int) -> Unit)? = null var onProgressHandler: ((Int) -> Unit)? = null
var onDownloadedHandler: ((List<String>) -> Unit)? = null var onDownloadedHandler: ((List<String>) -> Unit)? = null
var onErrorHandler: (() -> Unit)? = null var onErrorHandler: ((Exception) -> Unit)? = null
var onCompleteHandler: (() -> Unit)? = null var onCompleteHandler: (() -> Unit)? = null
var onNotifyChangedHandler: ((Boolean) -> Unit)? = null var onNotifyChangedHandler: ((Boolean) -> Unit)? = null
@@ -100,15 +100,13 @@ class GalleryDownloader(
} }
} }
//Could not retrieve reader if (reader.isNotEmpty()) {
if (reader.isEmpty()) //Save cache
throw IOException("Can't retrieve Reader") if (!cache.parentFile.exists())
cache.parentFile.mkdirs()
//Save cache cache.writeText(json.stringify(serializer, reader))
if (!cache.parentFile.exists()) }
cache.parentFile.mkdirs()
cache.writeText(json.stringify(serializer, reader))
reader reader
} }
@@ -120,6 +118,9 @@ class GalleryDownloader(
downloadJob = CoroutineScope(Dispatchers.Default).launch { downloadJob = CoroutineScope(Dispatchers.Default).launch {
val reader = reader.await() val reader = reader.await()
if (reader.isEmpty())
onErrorHandler?.invoke(IOException("Couldn't retrieve Reader"))
val list = ArrayList<String>() val list = ArrayList<String>()
onReaderLoadedHandler?.invoke(reader) onReaderLoadedHandler?.invoke(reader)
@@ -162,7 +163,7 @@ class GalleryDownloader(
} catch (e: Exception) { } catch (e: Exception) {
cache.delete() cache.delete()
onErrorHandler?.invoke() onErrorHandler?.invoke(e)
notificationBuilder notificationBuilder
.setContentTitle(galleryBlock.title) .setContentTitle(galleryBlock.title)

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#fff"
android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
<path
android:fillColor="#fff"
android:pathData="M8.5,15
a1.5,1.5 0 1,1 1.5,1.5
a1.5,1.5 0 0,1 -1.5,-1.5 Z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -22,7 +23,7 @@
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="64dp"
android:visibility="invisible" android:visibility="invisible"
android:background="@color/transparent" android:background="@color/transparent"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
@@ -46,22 +47,14 @@
android:text="@string/main_no_result" android:text="@string/main_no_result"
android:visibility="invisible"/> android:visibility="invisible"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_swipe_layout" android:id="@+id/main_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="-80dp"> android:paddingTop="64dp"
android:clipToPadding="false"
<androidx.recyclerview.widget.RecyclerView android:scrollbars="vertical"
android:id="@+id/main_recyclerview" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="80dp"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -71,7 +64,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:floatingSearch_searchBarMarginLeft="8dp" app:floatingSearch_searchBarMarginLeft="8dp"
app:floatingSearch_searchBarMarginRight="8dp" app:floatingSearch_searchBarMarginRight="8dp"
app:floatingSearch_searchBarMarginTop="24dp" app:floatingSearch_searchBarMarginTop="8dp"
app:floatingSearch_searchHint="@string/search_hint" app:floatingSearch_searchHint="@string/search_hint"
app:floatingSearch_suggestionsListAnimDuration="250" app:floatingSearch_suggestionsListAnimDuration="250"
app:floatingSearch_showSearchKey="true" app:floatingSearch_showSearchKey="true"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<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"
@@ -39,6 +40,7 @@
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"
android:layout_margin="16dp"
app:menu_colorNormal="@color/colorAccent"> app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
@@ -46,7 +48,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_downloading" android:src="@drawable/ic_downloading"
android:tint="@android:color/white"
app:fab_label="@string/reader_fab_download" app:fab_label="@string/reader_fab_download"
app:fab_size="mini"/> app:fab_size="mini"/>

View File

@@ -7,7 +7,7 @@
<TextView <TextView
style="?android:textAppearanceLarge" style="?android:textAppearanceLarge"
android:id="@+id/reader_dialog_title" android:id="@+id/dialog_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"
@@ -15,20 +15,20 @@
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"/>
<NumberPicker <NumberPicker
android:id="@+id/reader_dialog_number_picker" android:id="@+id/dialog_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/reader_dialog_title" app:layout_constraintTop_toBottomOf="@id/dialog_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/reader_dialog_ok" android:id="@+id/dialog_ok"
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/reader_dialog_number_picker" app:layout_constraintTop_toBottomOf="@id/dialog_number_picker"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<ImageView
android:id="@+id/icon_next"
android:contentDescription="@string/page_indicator_placeholder"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@drawable/ic_navigate_next_black_24dp"
android:tint="@color/colorAccent"
android:rotation="180"/>
<TextView
android:id="@+id/text_next"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<ImageView
android:id="@+id/icon_prev"
android:contentDescription="@string/page_indicator_placeholder"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@drawable/ic_navigate_next_black_24dp"
android:tint="@color/colorAccent"
android:rotation="180"/>
<TextView
android:id="@+id/text_prev"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>

View File

@@ -2,10 +2,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu 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">
<item <item android:id="@+id/main_menu_page_indicator"
android:id="@+id/main_menu_search" android:icon="@drawable/ic_jump"
android:icon="@drawable/ic_search" android:title="@string/page_indicator_placeholder"
android:title="@string/main_search"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item

View File

@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/reader_menu_page_indicator" <item android:id="@+id/reader_menu_page_indicator"
android:title="@string/reader_page_indicator_placeholder" android:title="@string/page_indicator_placeholder"
app:showAsAction="always|withText"/> app:showAsAction="always|withText"/>
</menu> </menu>

View File

@@ -6,12 +6,12 @@
<string name="main_no_result">結果なし</string> <string name="main_no_result">結果なし</string>
<string name="main_search">検索</string> <string name="main_search">検索</string>
<string name="search_hint">ギャラリー検索</string> <string name="search_hint">ギャラリー検索</string>
<string name="search_hint_with_page">ギャラリー検索</string>
<string name="settings_cache_title">キャッシュ</string> <string name="settings_cache_title">キャッシュ</string>
<string name="settings_clear_image_cache">イメージキャッシュクリア</string> <string name="settings_clear_image_cache">イメージキャッシュクリア</string>
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string> <string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string>
<string name="settings_clear_cache_summary">キャッシュサイズ: %1$d%2$s</string> <string name="settings_clear_cache_summary">キャッシュサイズ: %1$d%2$s</string>
<string name="settings_default_query">デフォルトキーワード</string> <string name="settings_default_query">デフォルトキーワード</string>
<string name="permission_explain">権限を拒否すると一部の機能が利用できません</string>
<string name="settings_galleries_per_page">一回にロードするギャラリー数</string> <string name="settings_galleries_per_page">一回にロードするギャラリー数</string>
<string name="settings_search_title">検索設定</string> <string name="settings_search_title">検索設定</string>
<string name="settings_title">設定</string> <string name="settings_title">設定</string>
@@ -53,4 +53,10 @@
<string name="reader_fab_download_cancel">バックグラウンドダウンロード中止</string> <string name="reader_fab_download_cancel">バックグラウンドダウンロード中止</string>
<string name="main_dialog_delete">このギャラリーを削除</string> <string name="main_dialog_delete">このギャラリーを削除</string>
<string name="main_drawer_downloads">ダウンロード</string> <string name="main_drawer_downloads">ダウンロード</string>
<string name="main_jump_title">ページ移動</string>
<string name="main_jump_message">現ページ番号: %1$d\nページ数: %2$d</string>
<string name="unable_to_connect">hitomi.laに接続できません</string>
<string name="main_move">%1$dページへ移動</string>
<string name="https_block_alert_title">(Korean only)</string>
<string name="https_block_alert">(Korean only)</string>
</resources> </resources>

View File

@@ -3,8 +3,8 @@
<string name="galleryblock_language">언어: %1$s</string> <string name="galleryblock_language">언어: %1$s</string>
<string name="galleryblock_series">시리즈: %1$s</string> <string name="galleryblock_series">시리즈: %1$s</string>
<string name="galleryblock_type">종류: %1$s</string> <string name="galleryblock_type">종류: %1$s</string>
<string name="permission_explain">권한을 거부하면 일부 기능이 작동하지 않을 수 있습니다</string>
<string name="search_hint">갤러리 검색</string> <string name="search_hint">갤러리 검색</string>
<string name="search_hint_with_page">갤러리 검색</string>
<string name="settings_default_query">기본 검색어</string> <string name="settings_default_query">기본 검색어</string>
<string name="settings_clear_image_cache">이미지 캐시 정리하기</string> <string name="settings_clear_image_cache">이미지 캐시 정리하기</string>
<string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?</string> <string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?</string>
@@ -53,4 +53,10 @@
<string name="reader_fab_download_cancel">백그라운드 다운로드 취소</string> <string name="reader_fab_download_cancel">백그라운드 다운로드 취소</string>
<string name="main_dialog_delete">갤러리 삭제</string> <string name="main_dialog_delete">갤러리 삭제</string>
<string name="main_drawer_downloads">다운로드</string> <string name="main_drawer_downloads">다운로드</string>
<string name="main_jump_title">페이지 이동</string>
<string name="main_jump_message">현재 페이지: %1$d\n페이지 수: %2$d</string>
<string name="unable_to_connect">hitomi.la에 연결할 수 없습니다</string>
<string name="main_move">%1$d 페이지로 이동</string>
<string name="https_block_alert_title">접속 불가 현상 안내</string>
<string name="https_block_alert">최근 https 차단으로 접속이 안 되는 경우가 발생하고 있습니다\n이 경우 플레이스토어에서 SNIper앱을 이용하시면 정상이용이 가능합니다.</string>
</resources> </resources>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowLightStatusBar">true</item>
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -12,7 +12,7 @@
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string> <string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
<string name="reader_imageview_description" translatable="false">Content ImageView</string> <string name="reader_imageview_description" translatable="false">Content ImageView</string>
<string name="reader_page_indicator_placeholder" translatable="false">-/-</string> <string name="page_indicator_placeholder" translatable="false">-/-</string>
<string name="plus_to_close" translatable="false">Fab</string> <string name="plus_to_close" translatable="false">Fab</string>
@@ -20,11 +20,14 @@
<string name="warning">Warning</string> <string name="warning">Warning</string>
<string name="permission_explain">Denying any permission can deactivate some functions</string> <string name="https_block_alert_title">(Korean only)</string>
<string name="https_block_alert">(Korean only)</string>
<string name="channel_download">Download</string> <string name="channel_download">Download</string>
<string name="channel_download_description">Shows download status</string> <string name="channel_download_description">Shows download status</string>
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
<string name="main_search">Search</string> <string name="main_search">Search</string>
<string name="main_no_result">No result</string> <string name="main_no_result">No result</string>
@@ -37,6 +40,11 @@
<string name="main_drawer_group_contact_github">Visit github</string> <string name="main_drawer_group_contact_github">Visit github</string>
<string name="main_drawer_group_contact_email">Email me!</string> <string name="main_drawer_group_contact_email">Email me!</string>
<string name="main_jump_title">Jump to page</string>
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
<string name="main_move">Move to page %1$d</string>
<string name="main_dialog_delete">Delete this gallery</string> <string name="main_dialog_delete">Delete this gallery</string>
<string name="help_dialog_title">WIP</string> <string name="help_dialog_title">WIP</string>
@@ -48,6 +56,7 @@
<string name="update_release_note"># Release Note(v%1$s)\n%2$s</string> <string name="update_release_note"># Release Note(v%1$s)\n%2$s</string>
<string name="search_hint">Search galleries</string> <string name="search_hint">Search galleries</string>
<string name="search_hint_with_page">Search galleries</string>
<string name="galleryblock_series">Series: %1$s</string> <string name="galleryblock_series">Series: %1$s</string>
<string name="galleryblock_type">Type: %1$s</string> <string name="galleryblock_type">Type: %1$s</string>

View File

@@ -10,7 +10,7 @@ import java.util.*
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
//galleryblock.js //galleryblock.js
fun fetchNozomi(area: String? = null, tag: String = "index", language: String = "all", start: Int = -1, count: Int = -1) : List<Int> { fun fetchNozomi(area: String? = null, tag: String = "index", language: String = "all", start: Int = -1, count: Int = -1) : Pair<List<Int>, Int> {
val url = val url =
when(area) { when(area) {
null -> "$protocol//$domain/$tag-$language$nozomiextension" null -> "$protocol//$domain/$tag-$language$nozomiextension"
@@ -28,6 +28,11 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String =
setRequestProperty("Range", "bytes=$startByte-$endByte") setRequestProperty("Range", "bytes=$startByte-$endByte")
} }
connect()
val totalItems = getHeaderField("Content-Range")
.replace(Regex("^[Bb]ytes \\d+-\\d+/"), "").toInt() / 4
val nozomi = ArrayList<Int>() val nozomi = ArrayList<Int>()
val arrayBuffer = ByteBuffer val arrayBuffer = ByteBuffer
@@ -37,10 +42,10 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String =
while (arrayBuffer.hasRemaining()) while (arrayBuffer.hasRemaining())
nozomi.add(arrayBuffer.int) nozomi.add(arrayBuffer.int)
return nozomi return Pair(nozomi, totalItems)
} }
} catch (e: Exception) { } catch (e: Exception) {
return emptyList() return Pair(emptyList(), 0)
} }
} }