1
.idea/vcs.xml
generated
@@ -2,6 +2,5 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/gh-pages" vcs="Git" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -7,10 +7,10 @@ android {
|
|||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 15
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 2
|
versionCode 3
|
||||||
versionName "1.1"
|
versionName "1.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -19,9 +19,14 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
def markwonVersion = "3.0.1"
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||||
@@ -34,6 +39,7 @@ dependencies {
|
|||||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation "ru.noties.markwon:core:${markwonVersion}"
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import xyz.quaver.hiyobi.getReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instrumented test, which will execute on an Android device.
|
* Instrumented test, which will execute on an Android device.
|
||||||
@@ -38,16 +40,7 @@ class ExampleInstrumentedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ExperimentalUnsignedTypes
|
|
||||||
fun test_doSearch() {
|
fun test_doSearch() {
|
||||||
Log.d("TEST", "Starting...")
|
|
||||||
|
|
||||||
runBlocking {
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
Log.d("TEST", "This is started! wow")
|
|
||||||
}.join()
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("TEST", "Finished! ...Really?")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.android.synthetic.main.activity_gallery.*
|
import kotlinx.android.synthetic.main.activity_gallery.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
@@ -32,9 +34,20 @@ class GalleryActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
setContentView(R.layout.activity_gallery)
|
setContentView(R.layout.activity_gallery)
|
||||||
|
|
||||||
|
supportActionBar?.title = intent.getStringExtra("GALLERY_TITLE")
|
||||||
|
|
||||||
galleryID = intent.getIntExtra("GALLERY_ID", 0)
|
galleryID = intent.getIntExtra("GALLERY_ID", 0)
|
||||||
CoroutineScope(Dispatchers.Unconfined).launch {
|
CoroutineScope(Dispatchers.Unconfined).launch {
|
||||||
reader = async(Dispatchers.IO) {
|
reader = async(Dispatchers.IO) {
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryActivity)
|
||||||
|
if (preference.getBoolean("use_hiyobi", false)) {
|
||||||
|
try {
|
||||||
|
xyz.quaver.hiyobi.getReader(galleryID)
|
||||||
|
Log.d("Pupil", "Using Hiyobi.me")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
getReader(galleryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
getReader(galleryID)
|
getReader(galleryID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +56,18 @@ class GalleryActivity : AppCompatActivity() {
|
|||||||
loadImages()
|
loadImages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
val preferences = android.preference.PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
|
if (preferences.getBoolean("security_mode", false))
|
||||||
|
window.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
else
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
loadJob?.cancel()
|
loadJob?.cancel()
|
||||||
@@ -78,17 +103,15 @@ class GalleryActivity : AppCompatActivity() {
|
|||||||
val reader = reader.await()
|
val reader = reader.await()
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
supportActionBar?.title = reader.title
|
|
||||||
|
|
||||||
with(gallery_progressbar) {
|
with(gallery_progressbar) {
|
||||||
max = reader.images.size
|
max = reader.size
|
||||||
progress = 0
|
progress = 0
|
||||||
|
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.images.chunked(8).forEach { chunked ->
|
reader.chunked(8).forEach { chunked ->
|
||||||
chunked.map {
|
chunked.map {
|
||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first
|
val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.content.pm.PackageManager
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
@@ -18,12 +19,15 @@ import android.text.style.AlignmentSpan
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.arlib.floatingsearchview.FloatingSearchView
|
import com.arlib.floatingsearchview.FloatingSearchView
|
||||||
@@ -31,36 +35,43 @@ import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
|||||||
import com.arlib.floatingsearchview.util.view.SearchInputView
|
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import kotlinx.android.synthetic.main.activity_main_content.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.content
|
||||||
|
import ru.noties.markwon.Markwon
|
||||||
import xyz.quaver.hitomi.*
|
import xyz.quaver.hitomi.*
|
||||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
import xyz.quaver.pupil.types.TagSuggestion
|
import xyz.quaver.pupil.types.TagSuggestion
|
||||||
|
import xyz.quaver.pupil.util.Histories
|
||||||
import xyz.quaver.pupil.util.SetLineOverlap
|
import xyz.quaver.pupil.util.SetLineOverlap
|
||||||
import xyz.quaver.pupil.util.checkUpdate
|
import xyz.quaver.pupil.util.checkUpdate
|
||||||
import xyz.quaver.pupil.util.getApkUrl
|
import xyz.quaver.pupil.util.getApkUrl
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.StringBuilder
|
||||||
|
import java.util.*
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val PERMISSION_REQUEST_CODE = 4585
|
private val permissionRequestCode = 4585
|
||||||
private val galleries = ArrayList<Pair<GalleryBlock, Bitmap?>>()
|
private val galleries = ArrayList<Pair<GalleryBlock, Bitmap?>>()
|
||||||
|
|
||||||
private var isLoading = false
|
|
||||||
private var query = ""
|
private var query = ""
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
private var galleryIDs: Deferred<List<Int>>? = null
|
||||||
super.onCreate(savedInstanceState)
|
private var loadingJob: Job? = null
|
||||||
|
|
||||||
window.setFlags(
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
WindowManager.LayoutParams.FLAG_SECURE,
|
Histories.default = Histories(File(cacheDir, "histories.json"))
|
||||||
WindowManager.LayoutParams.FLAG_SECURE)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
checkPermission()
|
checkPermission()
|
||||||
|
|
||||||
update()
|
checkUpdate()
|
||||||
|
|
||||||
main_appbar_layout.addOnOffsetChangedListener(
|
main_appbar_layout.addOnOffsetChangedListener(
|
||||||
AppBarLayout.OnOffsetChangedListener { _, p1 ->
|
AppBarLayout.OnOffsetChangedListener { _, p1 ->
|
||||||
@@ -73,16 +84,60 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setProgressViewOffset(false, 0, resources.getDimensionPixelSize(R.dimen.progress_view_offset))
|
setProgressViewOffset(false, 0, resources.getDimensionPixelSize(R.dimen.progress_view_offset))
|
||||||
|
|
||||||
setOnRefreshListener {
|
setOnRefreshListener {
|
||||||
runBlocking {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
cleanJob?.join()
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query)
|
||||||
|
loadBlocks()
|
||||||
}
|
}
|
||||||
fetchGalleries(query, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main_nav_view.setNavigationItemSelectedListener {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
main_drawer_layout.closeDrawers()
|
||||||
|
|
||||||
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
when(it.itemId) {
|
||||||
|
R.id.main_drawer_home -> {
|
||||||
|
query = query.replace("HISTORY", "")
|
||||||
|
fetchGalleries(query)
|
||||||
|
}
|
||||||
|
R.id.main_drawer_history -> {
|
||||||
|
query += "HISTORY"
|
||||||
|
fetchGalleries(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
setupSearchBar()
|
setupSearchBar()
|
||||||
fetchGalleries(query)
|
fetchGalleries(query)
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (main_drawer_layout.isDrawerOpen(GravityCompat.START))
|
||||||
|
main_drawer_layout.closeDrawer(GravityCompat.START)
|
||||||
|
else
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
|
if (preferences.getBoolean("security_mode", false))
|
||||||
|
window.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
else
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermission() {
|
private fun checkPermission() {
|
||||||
@@ -98,11 +153,54 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setPositiveButton(android.R.string.ok) { _, _ -> }
|
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
}.show()
|
}.show()
|
||||||
else
|
else
|
||||||
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE)
|
ActivityCompat.requestPermissions(this, permissions, permissionRequestCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun update() {
|
private fun checkUpdate() {
|
||||||
|
|
||||||
|
fun extractReleaseNote(update: JsonObject, locale: String) : String {
|
||||||
|
val markdown = update["body"]!!.content
|
||||||
|
|
||||||
|
val target = when(locale) {
|
||||||
|
"ko" -> "한국어"
|
||||||
|
"ja" -> "日本語"
|
||||||
|
else -> "English"
|
||||||
|
}
|
||||||
|
|
||||||
|
val releaseNote = Regex("^# Release Note.+$")
|
||||||
|
val language = Regex("^## $target$")
|
||||||
|
val end = Regex("^#.+$")
|
||||||
|
|
||||||
|
var releaseNoteFlag = false
|
||||||
|
var languageFlag = false
|
||||||
|
|
||||||
|
val result = StringBuilder()
|
||||||
|
|
||||||
|
for(line in markdown.split('\n')) {
|
||||||
|
if (releaseNote.matches(line)) {
|
||||||
|
releaseNoteFlag = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (releaseNoteFlag) {
|
||||||
|
if (language.matches(line)) {
|
||||||
|
languageFlag = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languageFlag) {
|
||||||
|
if (end.matches(line))
|
||||||
|
break
|
||||||
|
|
||||||
|
result.append(line+"\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getString(R.string.update_release_note, update["tag_name"]?.content, result.toString())
|
||||||
|
}
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -114,8 +212,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val dialog = AlertDialog.Builder(this@MainActivity).apply {
|
val dialog = AlertDialog.Builder(this@MainActivity).apply {
|
||||||
setTitle(R.string.update_title)
|
setTitle(R.string.update_title)
|
||||||
setMessage(getString(R.string.update_message, update["tag_name"], BuildConfig.VERSION_NAME))
|
val msg = extractReleaseNote(update, Locale.getDefault().language)
|
||||||
|
setMessage(Markwon.create(context).toMarkdown(msg))
|
||||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
|
Toast.makeText(
|
||||||
|
context, getString(R.string.update_download_started), Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
|
||||||
val dest = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
|
val dest = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
|
||||||
val desturi =
|
val desturi =
|
||||||
FileProvider.getUriForFile(
|
FileProvider.getUriForFile(
|
||||||
@@ -131,6 +234,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setDescription(getString(R.string.update_notification_description))
|
setDescription(getString(R.string.update_notification_description))
|
||||||
setTitle(getString(R.string.app_name))
|
setTitle(getString(R.string.app_name))
|
||||||
setDestinationUri(Uri.fromFile(dest))
|
setDestinationUri(Uri.fromFile(dest))
|
||||||
|
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
@@ -161,12 +265,15 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
with(main_recyclerview) {
|
with(main_recyclerview) {
|
||||||
adapter = GalleryBlockAdapter(galleries).apply {
|
adapter = GalleryBlockAdapter(galleries).apply {
|
||||||
setClickListener {
|
setClickListener { galleryID, title ->
|
||||||
val intent = Intent(this@MainActivity, GalleryActivity::class.java)
|
val intent = Intent(this@MainActivity, GalleryActivity::class.java)
|
||||||
intent.putExtra("GALLERY_ID", it)
|
intent.putExtra("GALLERY_ID", galleryID)
|
||||||
|
intent.putExtra("GALLERY_TITLE", title)
|
||||||
|
|
||||||
//TODO: Maybe sprinke some transitions will be nice :D
|
//TODO: Maybe sprinke some transitions will be nice :D
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|
||||||
|
Histories.default.add(galleryID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addOnScrollListener(
|
addOnScrollListener(
|
||||||
@@ -176,9 +283,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||||
|
|
||||||
if (!isLoading)
|
if (loadingJob?.isActive != true)
|
||||||
if (layoutManager.findLastCompletelyVisibleItemPosition() == galleries.size)
|
if (layoutManager.findLastCompletelyVisibleItemPosition() == galleries.size)
|
||||||
fetchGalleries(query)
|
loadBlocks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -295,35 +402,25 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (query != this@MainActivity.query) {
|
if (query != this@MainActivity.query) {
|
||||||
this@MainActivity.query = query
|
this@MainActivity.query = query
|
||||||
|
|
||||||
fetchGalleries(query, true)
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val cache = ArrayList<Int>()
|
attachNavigationDrawerToMenuButton(main_drawer_layout)
|
||||||
private var currentFetchingJob: Job? = null
|
}
|
||||||
private var cleanJob: Job? = null
|
}
|
||||||
|
|
||||||
private fun cancelFetch() {
|
private fun cancelFetch() {
|
||||||
isLoading = false
|
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
cleanJob?.join()
|
galleryIDs?.cancelAndJoin()
|
||||||
currentFetchingJob?.cancelAndJoin()
|
loadingJob?.cancelAndJoin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchGalleries(query: String, clear: Boolean = false) {
|
private fun clearGalleries() {
|
||||||
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
val perPage = preference.getString("per_page", "25")?.toInt() ?: 25
|
|
||||||
val defaultQuery = preference.getString("default_query", "")!!
|
|
||||||
|
|
||||||
if (clear) {
|
|
||||||
cancelFetch()
|
|
||||||
cleanJob = CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
cache.clear()
|
|
||||||
galleries.clear()
|
galleries.clear()
|
||||||
|
|
||||||
main_recyclerview.adapter?.notifyDataSetChanged()
|
main_recyclerview.adapter?.notifyDataSetChanged()
|
||||||
@@ -332,40 +429,59 @@ class MainActivity : AppCompatActivity() {
|
|||||||
main_progressbar.show()
|
main_progressbar.show()
|
||||||
main_swipe_layout.isRefreshing = false
|
main_swipe_layout.isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading)
|
private fun fetchGalleries(query: String, from: Int = 0) {
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val perPage = preference.getString("per_page", "25")?.toInt() ?: 25
|
||||||
|
val defaultQuery = preference.getString("default_query", "")!!
|
||||||
|
|
||||||
|
galleryIDs = null
|
||||||
|
|
||||||
|
if (galleryIDs?.isActive == true)
|
||||||
return
|
return
|
||||||
|
|
||||||
isLoading = true
|
galleryIDs = CoroutineScope(Dispatchers.IO).async {
|
||||||
|
when {
|
||||||
currentFetchingJob = CoroutineScope(Dispatchers.IO).launch {
|
query.contains("HISTORY") ->
|
||||||
try {
|
Histories.default.toList()
|
||||||
val galleryIDs: List<Int>
|
query.isEmpty() and defaultQuery.isEmpty() ->
|
||||||
|
fetchNozomi(start = from, count = perPage)
|
||||||
cleanJob?.join()
|
else ->
|
||||||
|
doSearch("$defaultQuery $query")
|
||||||
if (query.isEmpty() && defaultQuery.isEmpty())
|
}
|
||||||
galleryIDs = fetchNozomi(start = galleries.size, count = perPage)
|
|
||||||
else {
|
|
||||||
if (cache.isEmpty())
|
|
||||||
cache.addAll(doSearch("$defaultQuery $query"))
|
|
||||||
|
|
||||||
galleryIDs = cache.slice(galleries.size until Math.min(galleries.size + perPage, cache.size))
|
|
||||||
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
noMore = galleries.size + perPage >= cache.size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.isNotEmpty() and defaultQuery.isNotEmpty() and cache.isNullOrEmpty()) {
|
private fun loadBlocks() {
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val perPage = preference.getString("per_page", "25")?.toInt() ?: 25
|
||||||
|
val defaultQuery = preference.getString("default_query", "")!!
|
||||||
|
|
||||||
|
loadingJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val galleryIDs = galleryIDs?.await()
|
||||||
|
|
||||||
|
if (galleryIDs.isNullOrEmpty()) { //No result
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
main_noresult.visibility = View.VISIBLE
|
main_noresult.visibility = View.VISIBLE
|
||||||
main_progressbar.hide()
|
main_progressbar.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryIDs.chunked(4).forEach { chunked ->
|
if (query.isEmpty() and defaultQuery.isEmpty())
|
||||||
|
fetchGalleries("", galleries.size+perPage)
|
||||||
|
else
|
||||||
|
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
||||||
|
noMore = galleries.size + perPage >= galleryIDs.size
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
query.isEmpty() and defaultQuery.isEmpty() ->
|
||||||
|
galleryIDs
|
||||||
|
else ->
|
||||||
|
galleryIDs.slice(galleries.size until Math.min(galleries.size+perPage, galleryIDs.size))
|
||||||
|
}.chunked(4).forEach { chunked ->
|
||||||
chunked.map {
|
chunked.map {
|
||||||
async {
|
async {
|
||||||
val galleryBlock = getGalleryBlock(it)
|
val galleryBlock = getGalleryBlock(it)
|
||||||
@@ -388,9 +504,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.preference.PreferenceManager
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import xyz.quaver.pupil.util.Histories
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -25,6 +28,18 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
|
if (preferences.getBoolean("security_mode", false))
|
||||||
|
window.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
else
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
private val suffix = listOf(
|
private val suffix = listOf(
|
||||||
@@ -35,8 +50,8 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
"TB" //really?
|
"TB" //really?
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getCacheSize() : String {
|
private fun getCacheSize(dir: File) : String {
|
||||||
var size = context!!.cacheDir.walk().map { it.length() }.sum()
|
var size = dir.walk().map { it.length() }.sum()
|
||||||
var suffixIndex = 0
|
var suffixIndex = 0
|
||||||
|
|
||||||
while (size >= 1024) {
|
while (size >= 1024) {
|
||||||
@@ -50,22 +65,43 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||||
|
|
||||||
with(findPreference<Preference>("delete_cache")) {
|
with(findPreference<Preference>("delete_image_cache")) {
|
||||||
this ?: return@with
|
this ?: return@with
|
||||||
|
|
||||||
summary = getCacheSize()
|
val dir = File(context.cacheDir, "imageCache")
|
||||||
|
|
||||||
|
summary = getCacheSize(dir)
|
||||||
|
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
AlertDialog.Builder(context).apply {
|
AlertDialog.Builder(context).apply {
|
||||||
setTitle(R.string.warning)
|
setTitle(R.string.warning)
|
||||||
setMessage(R.string.settings_clear_cache_alert_message)
|
setMessage(R.string.settings_clear_cache_alert_message)
|
||||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
with(context.cacheDir) {
|
if (dir.exists())
|
||||||
if (exists())
|
dir.deleteRecursively()
|
||||||
deleteRecursively()
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = getCacheSize()
|
summary = getCacheSize(dir)
|
||||||
|
}
|
||||||
|
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with(findPreference<Preference>("clear_history")) {
|
||||||
|
this ?: return@with
|
||||||
|
|
||||||
|
val histories = Histories.default
|
||||||
|
|
||||||
|
summary = getString(R.string.settings_clear_history_summary, histories.size)
|
||||||
|
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
AlertDialog.Builder(context).apply {
|
||||||
|
setTitle(R.string.warning)
|
||||||
|
setMessage(R.string.settings_clear_history_alert_message)
|
||||||
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
|
histories.clear()
|
||||||
|
summary = getString(R.string.settings_clear_history_summary, histories.size)
|
||||||
}
|
}
|
||||||
setNegativeButton(android.R.string.no) { _, _ -> }
|
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||||
}.show()
|
}.show()
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Bitmap?
|
|||||||
class ViewHolder(val view: CardView) : RecyclerView.ViewHolder(view)
|
class ViewHolder(val view: CardView) : RecyclerView.ViewHolder(view)
|
||||||
class ProgressViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
|
class ProgressViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
private var callback: ((Int) -> Unit)? = null
|
private var callback: ((Int, String) -> Unit)? = null
|
||||||
fun setClickListener(callback: ((Int) -> Unit)?) {
|
fun setClickListener(callback: ((Int, String) -> Unit)?) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Bitmap?
|
|||||||
val series = gallery.series.ifEmpty { listOf("N/A") }
|
val series = gallery.series.ifEmpty { listOf("N/A") }
|
||||||
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
callback?.invoke(gallery.id)
|
callback?.invoke(gallery.id, gallery.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryblock_thumbnail.setImageBitmap(thumbnail)
|
galleryblock_thumbnail.setImageBitmap(thumbnail)
|
||||||
|
|||||||
74
app/src/main/java/xyz/quaver/pupil/util/history.kt
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
|
import kotlinx.serialization.parseList
|
||||||
|
import kotlinx.serialization.stringify
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
class Histories(private val file: File) : ArrayList<Int>() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!file.exists())
|
||||||
|
file.parentFile.mkdirs()
|
||||||
|
|
||||||
|
try {
|
||||||
|
load()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var default: Histories
|
||||||
|
|
||||||
|
fun load(file: File) : Histories {
|
||||||
|
return Histories(file).load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||||
|
fun load() : Histories {
|
||||||
|
return apply {
|
||||||
|
super.clear()
|
||||||
|
addAll(
|
||||||
|
Json(JsonConfiguration.Stable).parseList(
|
||||||
|
file.bufferedReader().use { it.readText() }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||||
|
fun save() {
|
||||||
|
file.writeText(Json(JsonConfiguration.Stable).stringify(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: Int): Boolean {
|
||||||
|
load()
|
||||||
|
|
||||||
|
if (contains(element))
|
||||||
|
super.remove(element)
|
||||||
|
|
||||||
|
super.add(0, element)
|
||||||
|
|
||||||
|
save()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: Int): Boolean {
|
||||||
|
load()
|
||||||
|
val retval = super.remove(element)
|
||||||
|
save()
|
||||||
|
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
super.clear()
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/main/res/drawable-anydpi/ic_history.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#333333">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable-anydpi/ic_home.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#333333">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_history.png
Normal file
|
After Width: | Height: | Size: 585 B |
BIN
app/src/main/res/drawable-hdpi/ic_home.png
Normal file
|
After Width: | Height: | Size: 279 B |
BIN
app/src/main/res/drawable-mdpi/ic_history.png
Normal file
|
After Width: | Height: | Size: 366 B |
BIN
app/src/main/res/drawable-mdpi/ic_home.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
app/src/main/res/drawable-xhdpi/ic_history.png
Normal file
|
After Width: | Height: | Size: 700 B |
BIN
app/src/main/res/drawable-xhdpi/ic_home.png
Normal file
|
After Width: | Height: | Size: 314 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_history.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_home.png
Normal file
|
After Width: | Height: | Size: 395 B |
BIN
app/src/main/res/drawable/side_nav_bar.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -1,83 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
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:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main_drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
android:fitsSystemWindows="true"
|
||||||
|
tools:openDrawer="start">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<include layout="@layout/activity_main_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.appbar.AppBarLayout
|
<com.google.android.material.navigation.NavigationView
|
||||||
android:id="@+id/main_appbar_layout"
|
android:id="@+id/main_nav_view"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:id="@+id/main_progressbar"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/main_noresult"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:text="@string/main_no_result"
|
|
||||||
android:visibility="invisible"/>
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
android:id="@+id/main_swipe_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginBottom="-64dp">
|
android:layout_gravity="start"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:headerLayout="@layout/nav_header_main"
|
||||||
|
app:menu="@menu/activity_main_drawer"/>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
android:id="@+id/main_recyclerview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingTop="64dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<com.arlib.floatingsearchview.FloatingSearchView
|
|
||||||
android:id="@+id/main_searchview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:floatingSearch_searchBarMarginLeft="8dp"
|
|
||||||
app:floatingSearch_searchBarMarginRight="8dp"
|
|
||||||
app:floatingSearch_searchBarMarginTop="8dp"
|
|
||||||
app:floatingSearch_searchHint="@string/search_hint"
|
|
||||||
app:floatingSearch_suggestionsListAnimDuration="250"
|
|
||||||
app:floatingSearch_showSearchKey="true"
|
|
||||||
app:floatingSearch_leftActionMode="noLeftAction"
|
|
||||||
app:floatingSearch_menu="@menu/main"
|
|
||||||
app:floatingSearch_dismissOnOutsideTouch="true"
|
|
||||||
app:floatingSearch_close_search_on_keyboard_dismiss="true"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
83
app/src/main/res/layout/activity_main_content.xml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/main_appbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:id="@+id/main_progressbar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_noresult"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/main_no_result"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/main_swipe_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="-64dp">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/main_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="64dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<com.arlib.floatingsearchview.FloatingSearchView
|
||||||
|
android:id="@+id/main_searchview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:floatingSearch_searchBarMarginLeft="8dp"
|
||||||
|
app:floatingSearch_searchBarMarginRight="8dp"
|
||||||
|
app:floatingSearch_searchBarMarginTop="8dp"
|
||||||
|
app:floatingSearch_searchHint="@string/search_hint"
|
||||||
|
app:floatingSearch_suggestionsListAnimDuration="250"
|
||||||
|
app:floatingSearch_showSearchKey="true"
|
||||||
|
app:floatingSearch_leftActionMode="showHamburger"
|
||||||
|
app:floatingSearch_menu="@menu/main"
|
||||||
|
app:floatingSearch_dismissOnOutsideTouch="true"
|
||||||
|
app:floatingSearch_close_search_on_keyboard_dismiss="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
12
app/src/main/res/layout/nav_header_main.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/nav_header_height"
|
||||||
|
android:background="@drawable/side_nav_bar"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:gravity="bottom"/>
|
||||||
15
app/src/main/res/menu/activity_main_drawer.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item android:id="@+id/main_drawer_home"
|
||||||
|
android:title="@string/main_drawer_home"
|
||||||
|
android:checked="true"
|
||||||
|
android:icon="@drawable/ic_home"/>
|
||||||
|
|
||||||
|
<item android:id="@+id/main_drawer_history"
|
||||||
|
android:title="@string/main_drawer_history"
|
||||||
|
android:icon="@drawable/ic_history"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Pupil</string>
|
|
||||||
<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>
|
||||||
@@ -8,7 +7,7 @@
|
|||||||
<string name="main_search">検索</string>
|
<string name="main_search">検索</string>
|
||||||
<string name="search_hint">ギャラリー検索</string>
|
<string name="search_hint">ギャラリー検索</string>
|
||||||
<string name="settings_cache_title">キャッシュ</string>
|
<string name="settings_cache_title">キャッシュ</string>
|
||||||
<string name="settings_clear_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>
|
||||||
@@ -16,8 +15,19 @@
|
|||||||
<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>
|
||||||
<string name="update_message">新バージョン%1$sをリリースしました\n(現バージョン: %2$s)\nアップデートしますか?</string>
|
|
||||||
<string name="update_notification_description">アップデートダウンロード中</string>
|
<string name="update_notification_description">アップデートダウンロード中</string>
|
||||||
<string name="update_title">新しいアップデートがあります</string>
|
<string name="update_title">新しいアップデートがあります</string>
|
||||||
<string name="warning">注意</string>
|
<string name="warning">注意</string>
|
||||||
|
<string name="settings_miscellaneous_title">その他</string>
|
||||||
|
<string name="settings_use_hiyobi_summary">ロード速度を向上させるためhiyobi.meからイメージロード</string>
|
||||||
|
<string name="settings_use_hiyobi_title">hiyobi.meからロード</string>
|
||||||
|
<string name="settings_clear_history">履歴の削除</string>
|
||||||
|
<string name="settings_clear_history_alert_message">履歴を削除しますか?</string>
|
||||||
|
<string name="settings_clear_history_summary">履歴数: %1$d</string>
|
||||||
|
<string name="main_drawer_history">履歴</string>
|
||||||
|
<string name="main_drawer_home">トップ</string>
|
||||||
|
<string name="update_download_started">ダウンロード中</string>
|
||||||
|
<string name="update_release_note"># リリースノート(v%1$s)\n%2$s</string>
|
||||||
|
<string name="settings_security_mode_title">セキュリティーモード</string>
|
||||||
|
<string name="settings_security_mode_summary">アプリ履歴でアプリの画面を表示しない</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,23 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Pupil</string>
|
|
||||||
<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="permission_explain">권한을 거부하면 일부 기능이 작동하지 않을 수 있습니다</string>
|
||||||
<string name="search_hint">갤러리 검색</string>
|
<string name="search_hint">갤러리 검색</string>
|
||||||
<string name="settings_default_query">기본 검색어</string>
|
<string name="settings_default_query">기본 검색어</string>
|
||||||
<string name="settings_clear_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_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>
|
||||||
<string name="update_message">버전 %1$s이 출시되었습니다.\n(현재 %2$s)\n업데이트 하시겠습니까?</string>
|
|
||||||
<string name="update_notification_description">apk 다운로드중…</string>
|
<string name="update_notification_description">apk 다운로드중…</string>
|
||||||
<string name="update_title">업데이트가 있습니다!</string>
|
<string name="update_title">업데이트가 있습니다!</string>
|
||||||
<string name="warning">경고</string>
|
<string name="warning">경고</string>
|
||||||
<string name="main_no_result">결과 없음</string>
|
<string name="main_no_result">결과 없음</string>
|
||||||
<string name="main_search">검색</string>
|
<string name="main_search">검색</string>
|
||||||
<string name="settings_cache_title">캐시</string>
|
<string name="settings_cache_title">캐시</string>
|
||||||
|
<string name="settings_miscellaneous_title">기타</string>
|
||||||
|
<string name="settings_use_hiyobi_summary">속도 향상을 위해 가능하면 hiyobi.me에서 이미지 로드</string>
|
||||||
|
<string name="settings_use_hiyobi_title">hiyobi.me 사용</string>
|
||||||
|
<string name="settings_clear_history">히스토리 삭제</string>
|
||||||
|
<string name="settings_clear_history_alert_message">히스토리를 삭제하시겠습니까?</string>
|
||||||
|
<string name="settings_clear_history_summary">히스토리 %1$d개 저장됨</string>
|
||||||
|
<string name="main_drawer_history">히스토리</string>
|
||||||
|
<string name="main_drawer_home">홈</string>
|
||||||
|
<string name="update_download_started">다운로드 중</string>
|
||||||
|
<string name="update_release_note"># 릴리즈 노트(v%1$s)\n%2$s</string>
|
||||||
|
<string name="settings_security_mode_summary">최근 앱 목록 창에서 앱 화면을 보이지 않게 합니다</string>
|
||||||
|
<string name="settings_security_mode_title">보안 모드 활성화</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
<item name="android:windowLightStatusBar">true</item>
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||||
<item name="android:statusBarColor">@color/transparent</item>
|
<item name="android:statusBarColor">@color/transparent</item>
|
||||||
|
|||||||
@@ -2,4 +2,9 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<dimen name="appbar_padding">64dp</dimen>
|
<dimen name="appbar_padding">64dp</dimen>
|
||||||
<dimen name="progress_view_offset">80dp</dimen>
|
<dimen name="progress_view_offset">80dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="nav_header_vertical_spacing">8dp</dimen>
|
||||||
|
<dimen name="nav_header_height">176dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -18,9 +18,13 @@
|
|||||||
<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>
|
||||||
|
|
||||||
|
<string name="main_drawer_home">Home</string>
|
||||||
|
<string name="main_drawer_history">History</string>
|
||||||
|
|
||||||
<string name="update_title">Update available</string>
|
<string name="update_title">Update available</string>
|
||||||
<string name="update_message">Version %1$s is available!\n(current version is %2$s)\nDo you want to update?</string>
|
<string name="update_download_started">Download started</string>
|
||||||
<string name="update_notification_description">Downloading apk…</string>
|
<string name="update_notification_description">Downloading apk…</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>
|
||||||
|
|
||||||
@@ -33,8 +37,16 @@
|
|||||||
<string name="settings_galleries_per_page">Galleries per page</string>
|
<string name="settings_galleries_per_page">Galleries per page</string>
|
||||||
<string name="settings_default_query">Default query</string>
|
<string name="settings_default_query">Default query</string>
|
||||||
<string name="settings_cache_title">Cache</string>
|
<string name="settings_cache_title">Cache</string>
|
||||||
<string name="settings_clear_cache">Clear cache</string>
|
<string name="settings_clear_image_cache">Clear image cache</string>
|
||||||
<string name="settings_clear_cache_summary">Currently using %1$d%2$s of cache</string>
|
<string name="settings_clear_cache_summary">Currently using %1$d%2$s of cache</string>
|
||||||
<string name="settings_clear_cache_alert_message">Deleting cache can affect image loading speed. Do you want to continue?</string>
|
<string name="settings_clear_cache_alert_message">Deleting cache can affect image loading speed. Do you want to continue?</string>
|
||||||
|
<string name="settings_clear_history">Clear history</string>
|
||||||
|
<string name="settings_clear_history_alert_message">Do you want to clear histories?</string>
|
||||||
|
<string name="settings_clear_history_summary">%1$d histories saved</string>
|
||||||
|
<string name="settings_miscellaneous_title">Miscellaneous</string>
|
||||||
|
<string name="settings_use_hiyobi_title">Use hiyobi.me</string>
|
||||||
|
<string name="settings_use_hiyobi_summary">Load images from hiyobi.me to improve loading speed (if available)</string>
|
||||||
|
<string name="settings_security_mode_title">Enable security mode</string>
|
||||||
|
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -25,8 +25,28 @@
|
|||||||
app:title="@string/settings_cache_title">
|
app:title="@string/settings_cache_title">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:title="@string/settings_clear_cache"
|
app:title="@string/settings_clear_image_cache"
|
||||||
app:key="delete_cache"/>
|
app:key="delete_image_cache"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:title="@string/settings_clear_history"
|
||||||
|
app:key="clear_history"/>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
app:title="@string/settings_miscellaneous_title">
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
app:key="use_hiyobi"
|
||||||
|
app:title="@string/settings_use_hiyobi_title"
|
||||||
|
app:summary="@string/settings_use_hiyobi_summary"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
app:key="security_mode"
|
||||||
|
app:title="@string/settings_security_mode_title"
|
||||||
|
app:summary="@string/settings_security_mode_summary"
|
||||||
|
app:defaultValue="true"/>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import xyz.quaver.pupil.util.checkUpdate
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
@@ -12,8 +12,9 @@ import xyz.quaver.pupil.util.checkUpdate
|
|||||||
class ExampleUnitTest {
|
class ExampleUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ImplicitReflectionSerializer
|
||||||
fun test() {
|
fun test() {
|
||||||
print(checkUpdate("https://api.github.com/repos/tom5079/Pupil-issue/releases", "0.0.1"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import kotlinx.serialization.list
|
import kotlinx.serialization.list
|
||||||
import kotlinx.serialization.parseList
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@@ -18,10 +16,7 @@ data class GalleryInfo(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val height: Int
|
val height: Int
|
||||||
)
|
)
|
||||||
data class Reader(
|
typealias Reader = List<Pair<URL, GalleryInfo?>>
|
||||||
val title: String,
|
|
||||||
val images: List<Pair<URL, GalleryInfo?>>
|
|
||||||
)
|
|
||||||
//Set header `Referer` to reader url to avoid 403 error
|
//Set header `Referer` to reader url to avoid 403 error
|
||||||
fun getReader(galleryID: Int) : Reader {
|
fun getReader(galleryID: Int) : Reader {
|
||||||
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
|
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
|
||||||
@@ -29,8 +24,6 @@ fun getReader(galleryID: Int) : Reader {
|
|||||||
|
|
||||||
val doc = Jsoup.connect(readerUrl).get()
|
val doc = Jsoup.connect(readerUrl).get()
|
||||||
|
|
||||||
val title = doc.selectFirst("title").text()
|
|
||||||
|
|
||||||
val images = doc.select(".img-url").map {
|
val images = doc.select(".img-url").map {
|
||||||
URL(protocol + urlFromURL(it.text()))
|
URL(protocol + urlFromURL(it.text()))
|
||||||
}
|
}
|
||||||
@@ -49,5 +42,5 @@ fun getReader(galleryID: Int) : Reader {
|
|||||||
if (images.size > galleryInfo.size)
|
if (images.size > galleryInfo.size)
|
||||||
galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size))
|
galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size))
|
||||||
|
|
||||||
return Reader(title, images zip galleryInfo)
|
return images zip galleryInfo
|
||||||
}
|
}
|
||||||
47
libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package xyz.quaver.hiyobi
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
|
import kotlinx.serialization.json.content
|
||||||
|
import xyz.quaver.hitomi.Reader
|
||||||
|
import java.net.URL
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
|
const val hiyobi = "xn--9w3b15m8vo.asia"
|
||||||
|
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
||||||
|
|
||||||
|
var cookie: String = ""
|
||||||
|
|
||||||
|
fun renewCookie() : String {
|
||||||
|
val url = "https://$hiyobi/"
|
||||||
|
|
||||||
|
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||||
|
setRequestProperty("User-Agent", user_agent)
|
||||||
|
connectTimeout = 2000
|
||||||
|
connect()
|
||||||
|
return headerFields["Set-Cookie"]!![0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getReader(galleryId: Int) : Reader {
|
||||||
|
val url = "https://$hiyobi/data/json/${galleryId}_list.json"
|
||||||
|
|
||||||
|
if (cookie.isEmpty())
|
||||||
|
cookie = renewCookie()
|
||||||
|
|
||||||
|
val json = Json(JsonConfiguration.Stable).parseJson(
|
||||||
|
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||||
|
setRequestProperty("User-Agent", user_agent)
|
||||||
|
setRequestProperty("Cookie", cookie)
|
||||||
|
connectTimeout = 2000
|
||||||
|
connect()
|
||||||
|
|
||||||
|
inputStream.bufferedReader().use { it.readText() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return json.jsonArray.map {
|
||||||
|
val name = it.jsonObject["name"]!!.content
|
||||||
|
Pair(URL("https://$hiyobi/data/$galleryId/$name"), null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,4 +61,9 @@ class UnitTest {
|
|||||||
|
|
||||||
print(reader)
|
print(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_hiyobi() {
|
||||||
|
xyz.quaver.hiyobi.getReader(1414061)
|
||||||
|
}
|
||||||
}
|
}
|
||||||