diff --git a/app/build.gradle b/app/build.gradle
index 396af997..64729574 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,8 +9,8 @@ android {
applicationId "xyz.quaver.pupil"
minSdkVersion 16
targetSdkVersion 28
- versionCode 3
- versionName "1.2"
+ versionCode 4
+ versionName "1.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -40,6 +40,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "ru.noties.markwon:core:${markwonVersion}"
+ implementation 'com.shawnlin:number-picker:2.4.8'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
index 5a6f14f7..f6cbca02 100644
--- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
@@ -4,16 +4,10 @@ import android.graphics.BitmapFactory
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import xyz.quaver.hiyobi.getReader
import java.io.File
-import java.util.*
/**
* Instrumented test, which will execute on an Android device.
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1bdffc64..0e380367 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,8 +3,6 @@
package="xyz.quaver.pupil">
-
-
-
()
- private var galleryID = 0
- private lateinit var reader: Deferred
- private var loadJob: Job? = null
- private var screenMode = 0
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- window.setFlags(
- WindowManager.LayoutParams.FLAG_SECURE,
- WindowManager.LayoutParams.FLAG_SECURE)
-
- setContentView(R.layout.activity_gallery)
-
- supportActionBar?.title = intent.getStringExtra("GALLERY_TITLE")
-
- galleryID = intent.getIntExtra("GALLERY_ID", 0)
- CoroutineScope(Dispatchers.Unconfined).launch {
- 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)
- }
- }
-
- initView()
- 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() {
- super.onDestroy()
- loadJob?.cancel()
- }
-
- private fun initView() {
- gallery_recyclerview.adapter = GalleryAdapter(images).apply {
- setOnClick {
- val attrs = window.attributes
-
- screenMode = (screenMode+1)%2
-
- when(screenMode) {
- 0 -> {
- attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
- supportActionBar?.show()
- }
- 1 -> {
- attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
- supportActionBar?.hide()
- }
- }
- window.attributes = attrs
- }
- }
- }
-
- private fun loadImages() {
-
- fun webpUrlFromUrl(url: URL) = URL(url.toString().replace("/galleries/", "/webp/") + ".webp")
-
- loadJob = CoroutineScope(Dispatchers.Default).launch {
- val reader = reader.await()
-
- launch(Dispatchers.Main) {
- with(gallery_progressbar) {
- max = reader.size
- progress = 0
-
- visibility = View.VISIBLE
- }
- }
-
- reader.chunked(8).forEach { chunked ->
- chunked.map {
- async(Dispatchers.IO) {
- val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first
-
- val fileName: String
-
- with(url.path) {
- fileName = substring(lastIndexOf('/')+1)
- }
-
- val cache = File(cacheDir, "/imageCache/$galleryID/$fileName")
-
- if (!cache.exists())
- with(url.openConnection() as HttpsURLConnection) {
- setRequestProperty("Referer", getReferer(galleryID))
-
- if (!cache.parentFile.exists())
- cache.parentFile.mkdirs()
-
- inputStream.copyTo(FileOutputStream(cache))
- }
-
- cache.absolutePath
- }
- }.forEach {
- val cache = it.await()
-
- launch(Dispatchers.Main) {
- images.add(cache)
- gallery_recyclerview.adapter?.notifyItemInserted(images.size - 1)
- gallery_progressbar.progress++
- }
- }
- }
-
- launch(Dispatchers.Main) {
- gallery_progressbar.visibility = View.GONE
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
index 65ea4cec..194bfb15 100644
--- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
@@ -1,31 +1,15 @@
package xyz.quaver.pupil
-import android.Manifest
-import android.app.DownloadManager
-import android.content.BroadcastReceiver
-import android.content.Context
import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.PackageManager
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
import android.net.Uri
-import android.os.Build
import android.os.Bundle
-import android.os.Environment
import android.preference.PreferenceManager
import android.text.*
import android.text.style.AlignmentSpan
-import android.util.Log
import android.view.View
import android.view.WindowManager
-import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import androidx.core.content.FileProvider
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
@@ -44,19 +28,18 @@ import xyz.quaver.hitomi.*
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.types.TagSuggestion
import xyz.quaver.pupil.util.Histories
+import xyz.quaver.pupil.util.ItemClickSupport
import xyz.quaver.pupil.util.SetLineOverlap
import xyz.quaver.pupil.util.checkUpdate
-import xyz.quaver.pupil.util.getApkUrl
import java.io.File
-import java.lang.StringBuilder
+import java.io.FileOutputStream
import java.util.*
import javax.net.ssl.HttpsURLConnection
import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity() {
- private val permissionRequestCode = 4585
- private val galleries = ArrayList>()
+ private val galleries = ArrayList>>()
private var query = ""
@@ -67,9 +50,12 @@ class MainActivity : AppCompatActivity() {
Histories.default = Histories(File(cacheDir, "histories.json"))
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ )
- checkPermission()
+ setContentView(R.layout.activity_main)
checkUpdate()
@@ -81,7 +67,11 @@ class MainActivity : AppCompatActivity() {
)
with(main_swipe_layout) {
- setProgressViewOffset(false, 0, resources.getDimensionPixelSize(R.dimen.progress_view_offset))
+ setProgressViewOffset(
+ false,
+ resources.getDimensionPixelSize(R.dimen.progress_view_start),
+ resources.getDimensionPixelSize(R.dimen.progress_view_offset)
+ )
setOnRefreshListener {
CoroutineScope(Dispatchers.Main).launch {
@@ -97,17 +87,36 @@ class MainActivity : AppCompatActivity() {
CoroutineScope(Dispatchers.Main).launch {
main_drawer_layout.closeDrawers()
- cancelFetch()
- clearGalleries()
when(it.itemId) {
R.id.main_drawer_home -> {
+ cancelFetch()
+ clearGalleries()
query = query.replace("HISTORY", "")
fetchGalleries(query)
}
R.id.main_drawer_history -> {
+ cancelFetch()
+ clearGalleries()
query += "HISTORY"
fetchGalleries(query)
}
+ R.id.main_drawer_help -> {
+ AlertDialog.Builder(this@MainActivity).apply {
+ title = getString(R.string.help_dialog_title)
+ setMessage(R.string.help_dialog_message)
+
+ setPositiveButton(android.R.string.ok) { _, _ -> }
+ }.show()
+ }
+ R.id.main_drawer_github -> {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
+ }
+ R.id.main_drawer_homepage -> {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page))))
+ }
+ R.id.main_drawer_email -> {
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
+ }
}
loadBlocks()
}
@@ -140,23 +149,6 @@ class MainActivity : AppCompatActivity() {
super.onResume()
}
- private fun checkPermission() {
- val permissions = arrayOf(
- Manifest.permission.WRITE_EXTERNAL_STORAGE
- )
-
- if (permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) {
- if (permissions.any { ActivityCompat.shouldShowRequestPermissionRationale(this, it) })
- AlertDialog.Builder(this).apply {
- setTitle(R.string.warning)
- setMessage(R.string.permission_explain)
- setPositiveButton(android.R.string.ok) { _, _ -> }
- }.show()
- else
- ActivityCompat.requestPermissions(this, permissions, permissionRequestCode)
- }
- }
-
private fun checkUpdate() {
fun extractReleaseNote(update: JsonObject, locale: String) : String {
@@ -177,7 +169,7 @@ class MainActivity : AppCompatActivity() {
val result = StringBuilder()
- for(line in markdown.split('\n')) {
+ for(line in markdown.lines()) {
if (releaseNote.matches(line)) {
releaseNoteFlag = true
continue
@@ -201,57 +193,16 @@ class MainActivity : AppCompatActivity() {
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)
- return
-
CoroutineScope(Dispatchers.Default).launch {
val update =
checkUpdate(getString(R.string.release_url), BuildConfig.VERSION_NAME) ?: return@launch
- val (url, fileName) = getApkUrl(update, getString(R.string.release_name)) ?: return@launch
-
val dialog = AlertDialog.Builder(this@MainActivity).apply {
setTitle(R.string.update_title)
val msg = extractReleaseNote(update, Locale.getDefault().language)
setMessage(Markwon.create(context).toMarkdown(msg))
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 desturi =
- FileProvider.getUriForFile(
- applicationContext,
- "xyz.quaver.pupil.provider",
- dest
- )
-
- if (dest.exists())
- dest.delete()
-
- val request = DownloadManager.Request(Uri.parse(url)).apply {
- setDescription(getString(R.string.update_notification_description))
- setTitle(getString(R.string.app_name))
- setDestinationUri(Uri.fromFile(dest))
- setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
- }
-
- val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
- val id = manager.enqueue(request)
-
- registerReceiver(object: BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- val install = Intent(Intent.ACTION_VIEW).apply {
- flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
- setDataAndType(desturi, manager.getMimeTypeForDownloadedFile(id))
- }
-
- startActivity(install)
- unregisterReceiver(this)
- finish()
- }
- }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
+ startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page))))
}
setNegativeButton(android.R.string.no) { _, _ ->}
}
@@ -264,18 +215,7 @@ class MainActivity : AppCompatActivity() {
private fun setupRecyclerView() {
with(main_recyclerview) {
- adapter = GalleryBlockAdapter(galleries).apply {
- setClickListener { galleryID, title ->
- val intent = Intent(this@MainActivity, GalleryActivity::class.java)
- intent.putExtra("GALLERY_ID", galleryID)
- intent.putExtra("GALLERY_TITLE", title)
-
- //TODO: Maybe sprinke some transitions will be nice :D
- startActivity(intent)
-
- Histories.default.add(galleryID)
- }
- }
+ adapter = GalleryBlockAdapter(galleries)
addOnScrollListener(
object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@@ -289,6 +229,17 @@ class MainActivity : AppCompatActivity() {
}
}
)
+ ItemClickSupport.addTo(this).setOnItemClickListener { _, position, _ ->
+ val intent = Intent(this@MainActivity, ReaderActivity::class.java)
+ val gallery = galleries[position].first
+ intent.putExtra("GALLERY_ID", gallery.id)
+ intent.putExtra("GALLERY_TITLE", gallery.title)
+
+ //TODO: Maybe sprinke some transitions will be nice :D
+ startActivity(intent)
+
+ Histories.default.add(gallery.id)
+ }
}
}
@@ -402,9 +353,11 @@ class MainActivity : AppCompatActivity() {
if (query != this@MainActivity.query) {
this@MainActivity.query = query
- cancelFetch()
- clearGalleries()
- fetchGalleries(query)
+ CoroutineScope(Dispatchers.Main).launch {
+ cancelFetch()
+ clearGalleries()
+ fetchGalleries(query)
+ }
}
}
})
@@ -481,29 +434,43 @@ class MainActivity : AppCompatActivity() {
galleryIDs
else ->
galleryIDs.slice(galleries.size until Math.min(galleries.size+perPage, galleryIDs.size))
- }.chunked(4).forEach { chunked ->
- chunked.map {
- async {
- val galleryBlock = getGalleryBlock(it)
- val thumbnail: Bitmap
+ }.chunked(4).let { chunks ->
+ for (chunk in chunks)
+ chunk.map {
+ async {
+ try {
+ val galleryBlock = getGalleryBlock(it)
- with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) {
- thumbnail = BitmapFactory.decodeStream(inputStream)
+ val thumbnail = async {
+ val cache = File(cacheDir, "imageCache/$it/thumbnail.${galleryBlock.thumbnails[0].path.split('.').last()}")
+
+ if (!cache.exists())
+ with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) {
+ if (!cache.parentFile.exists())
+ cache.parentFile.mkdirs()
+
+ inputStream.copyTo(FileOutputStream(cache))
+ }
+
+ cache.absolutePath
+ }
+
+ Pair(galleryBlock, thumbnail)
+ } catch (e: Exception) {
+ null
+ }
}
+ }.forEach {
+ val galleryBlock = it.await() ?: return@forEach
- Pair(galleryBlock, thumbnail)
- }
- }.forEach {
- val galleryBlock = it.await()
+ withContext(Dispatchers.Main) {
+ main_progressbar.hide()
- withContext(Dispatchers.Main) {
- main_progressbar.hide()
-
- galleries.add(galleryBlock)
- main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1)
+ galleries.add(galleryBlock)
+ main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1)
+ }
}
}
- }
}
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt
new file mode 100644
index 00000000..80471c11
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt
@@ -0,0 +1,254 @@
+package xyz.quaver.pupil
+
+import android.os.Bundle
+import android.util.Log
+import android.view.*
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.preference.PreferenceManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.PagerSnapHelper
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.android.synthetic.main.activity_reader.*
+import kotlinx.android.synthetic.main.activity_reader.view.*
+import kotlinx.android.synthetic.main.dialog_numberpicker.view.*
+import kotlinx.coroutines.*
+import xyz.quaver.hitomi.Reader
+import xyz.quaver.hitomi.getReader
+import xyz.quaver.hitomi.getReferer
+import xyz.quaver.pupil.adapters.ReaderAdapter
+import xyz.quaver.pupil.util.ItemClickSupport
+import java.io.File
+import java.io.FileOutputStream
+import java.net.URL
+import javax.net.ssl.HttpsURLConnection
+
+class ReaderActivity : AppCompatActivity() {
+
+ private val images = ArrayList()
+ private var galleryID = 0
+ private var gallerySize: Int = 0
+ private var currentPage: Int = 0
+ private lateinit var reader: Deferred
+ private var loadJob: Job? = null
+
+ private lateinit var snapHelper: PagerSnapHelper
+
+ private var menu: Menu? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE)
+
+ setContentView(R.layout.activity_reader)
+
+ supportActionBar?.title = intent.getStringExtra("GALLERY_TITLE")
+
+ galleryID = intent.getIntExtra("GALLERY_ID", 0)
+ CoroutineScope(Dispatchers.Unconfined).launch {
+ reader = async(Dispatchers.IO) {
+ val preference = PreferenceManager.getDefaultSharedPreferences(this@ReaderActivity)
+ if (preference.getBoolean("use_hiyobi", false)) {
+ try {
+ xyz.quaver.hiyobi.getReader(galleryID)
+ } catch (e: Exception) {
+ getReader(galleryID)
+ }
+ }
+ getReader(galleryID)
+ }
+ }
+
+ snapHelper = PagerSnapHelper()
+
+ val preferences = PreferenceManager.getDefaultSharedPreferences(this)
+
+ val attrs = window.attributes
+
+ if (preferences.getBoolean("reader_fullscreen", false)) {
+ attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
+ supportActionBar?.hide()
+ } else {
+ attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
+ supportActionBar?.show()
+ }
+
+ window.attributes = attrs
+
+ if (preferences.getBoolean("reader_one_by_one", false)) {
+ snapHelper.attachToRecyclerView(reader_recyclerview)
+ reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
+ } else {
+ snapHelper.attachToRecyclerView(null)
+ reader_recyclerview.layoutManager = LinearLayoutManager(this)
+ }
+
+ initView()
+ loadImages()
+ }
+
+ 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()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.reader, menu)
+ this.menu = menu
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ when(item?.itemId) {
+ R.id.reader_menu_page_indicator -> {
+ val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
+ with(view.reader_dialog_number_picker) {
+ minValue=1
+ maxValue=gallerySize
+ value=currentPage
+ }
+ val dialog = AlertDialog.Builder(this).apply {
+ setView(view)
+ }.create()
+ view.reader_dialog_ok.setOnClickListener {
+ (reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(view.reader_dialog_number_picker.value-1, 0)
+ dialog.dismiss()
+ }
+
+ dialog.show()
+ }
+ }
+
+ return true
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ loadJob?.cancel()
+ }
+
+ private fun initView() {
+ with(reader_recyclerview) {
+ adapter = ReaderAdapter(images)
+
+ 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 (layoutManager.findFirstVisibleItemPosition() == -1)
+ return
+ currentPage = layoutManager.findFirstVisibleItemPosition()+1
+ menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize"
+ }
+ })
+
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ ItemClickSupport.addTo(this)
+ .setOnItemClickListener { _, _, _ ->
+ val attrs = window.attributes
+ val fullscreen = preferences.getBoolean("reader_fullscreen", false)
+
+ if (fullscreen) {
+ attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
+ supportActionBar?.show()
+ } else {
+ attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
+ supportActionBar?.hide()
+ }
+
+ window.attributes = attrs
+
+ preferences.edit().putBoolean("reader_fullscreen", !fullscreen).apply()
+ }.setOnItemLongClickListener { _, _, _ ->
+ val oneByOne = preferences.getBoolean("reader_one_by_one", false)
+ if (oneByOne) {
+ snapHelper.attachToRecyclerView(null)
+ reader_recyclerview.layoutManager = LinearLayoutManager(context)
+ }
+ else {
+ snapHelper.attachToRecyclerView(reader_recyclerview)
+ reader_recyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+ }
+
+ (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
+
+ preferences.edit().putBoolean("reader_one_by_one", !oneByOne).apply()
+
+ true
+ }
+ }
+ }
+
+ private fun loadImages() {
+ fun webpUrlFromUrl(url: URL) = URL(url.toString().replace("/galleries/", "/webp/") + ".webp")
+
+ loadJob = CoroutineScope(Dispatchers.Default).launch {
+ val reader = reader.await()
+
+ launch(Dispatchers.Main) {
+ with(reader_progressbar) {
+ max = reader.size
+ progress = 0
+
+ visibility = View.VISIBLE
+ }
+
+ gallerySize = reader.size
+ menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize"
+ }
+
+ reader.chunked(8).forEach { chunked ->
+ chunked.map {
+ async(Dispatchers.IO) {
+ val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first
+
+ val fileName: String
+
+ with(url.path) {
+ fileName = substring(lastIndexOf('/')+1)
+ }
+
+ val cache = File(cacheDir, "/imageCache/$galleryID/$fileName")
+
+ if (!cache.exists())
+ with(url.openConnection() as HttpsURLConnection) {
+ setRequestProperty("Referer", getReferer(galleryID))
+
+ if (!cache.parentFile.exists())
+ cache.parentFile.mkdirs()
+
+ inputStream.copyTo(FileOutputStream(cache))
+ }
+
+ cache.absolutePath
+ }
+ }.forEach {
+ val cache = it.await()
+
+ launch(Dispatchers.Main) {
+ images.add(cache)
+ reader_recyclerview.adapter?.notifyItemInserted(images.size - 1)
+ reader_progressbar.progress++
+ }
+ }
+ }
+
+ launch(Dispatchers.Main) {
+ reader_progressbar.visibility = View.GONE
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt
index d5c301ce..1640e5d5 100644
--- a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt
@@ -2,12 +2,22 @@ package xyz.quaver.pupil
import android.os.Bundle
import android.preference.PreferenceManager
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
import android.view.MenuItem
+import android.view.View
import android.view.WindowManager
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import kotlinx.android.synthetic.main.dialog_default_query.*
+import kotlinx.android.synthetic.main.dialog_default_query.view.*
+import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.util.Histories
import java.io.File
@@ -109,6 +119,120 @@ class SettingsActivity : AppCompatActivity() {
true
}
}
+
+ with(findPreference("default_query")) {
+ this ?: return@with
+
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+ summary = preferences.getString("default_query", "") ?: ""
+
+ val languages = resources.getStringArray(R.array.languages).map {
+ it.split("|").let { split ->
+ Pair(split[0], split[1])
+ }
+ }.toMap()
+ val reverseLanguages = languages.entries.associate { (k, v) -> v to k }
+
+ val excludeBL = "-male:yaoi"
+ val excludeGuro = listOf("-female:guro", "-male:guro")
+
+ setOnPreferenceClickListener {
+ val dialogView = LayoutInflater.from(context).inflate(
+ R.layout.dialog_default_query,
+ null
+ )
+
+ val tags = Tags.parse(
+ preferences.getString("default_query", "") ?: ""
+ )
+
+ summary = tags.toString()
+
+ with(dialogView.default_query_dialog_language_selector) {
+ adapter =
+ ArrayAdapter(
+ context,
+ android.R.layout.simple_spinner_dropdown_item,
+ arrayListOf(
+ getString(R.string.default_query_dialog_language_selector_none)
+ ).apply {
+ addAll(languages.values)
+ }
+ )
+ if (tags.any { it.area == "language" }) {
+ val tag = languages[tags.first { it.area == "language" }.tag]
+ if (tag != null) {
+ setSelection(
+ (adapter as ArrayAdapter).getPosition(tag)
+ )
+ tags.removeByArea("language")
+ }
+ }
+ }
+
+ with(dialogView.default_query_dialog_BL_checkbox) {
+ isChecked = tags.contains(excludeBL)
+ if (tags.contains(excludeBL))
+ tags.remove(excludeBL)
+ }
+
+ with(dialogView.default_query_dialog_guro_checkbox) {
+ isChecked = excludeGuro.all { tags.contains(it) }
+ if (excludeGuro.all { tags.contains(it) })
+ excludeGuro.forEach {
+ tags.remove(it)
+ }
+ }
+
+ with(dialogView.default_query_dialog_edittext) {
+ setText(tags.toString(), TextView.BufferType.EDITABLE)
+ addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(s: Editable?) {
+ s ?: return
+
+ if (s.any { it.isUpperCase() })
+ s.replace(0, s.length, s.toString().toLowerCase())
+ }
+ })
+ }
+
+ val dialog = AlertDialog.Builder(context!!).apply {
+ setView(dialogView)
+ }.create()
+
+ dialogView.default_query_dialog_ok.setOnClickListener {
+ val newTags = Tags.parse(dialogView.default_query_dialog_edittext.text.toString())
+
+ with(dialogView.default_query_dialog_language_selector) {
+ if (selectedItemPosition != 0)
+ newTags.add("language:${reverseLanguages[selectedItem]}")
+ }
+
+ if (dialogView.default_query_dialog_BL_checkbox.isChecked)
+ newTags.add(excludeBL)
+
+ if (dialogView.default_query_dialog_guro_checkbox.isChecked)
+ excludeGuro.forEach { tag ->
+ newTags.add(tag)
+ }
+
+ preferenceManager.sharedPreferences.edit().putString("default_query", newTags.toString()).apply()
+ summary = preferences.getString("default_query", "") ?: ""
+ tags.clear()
+ tags.addAll(newTags)
+ dialog.dismiss()
+ }
+
+ dialog.show()
+
+ true
+ }
+ }
}
}
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt
index 71851505..e83e4f86 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt
@@ -1,18 +1,21 @@
package xyz.quaver.pupil.adapters
-import android.graphics.Bitmap
+import android.graphics.BitmapFactory
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.item_galleryblock.view.*
+import kotlinx.coroutines.*
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.toTag
import xyz.quaver.pupil.R
+import java.io.File
-class GalleryBlockAdapter(private val galleries: List>) : RecyclerView.Adapter() {
+class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() {
private enum class ViewType {
VIEW_ITEM,
@@ -33,11 +36,6 @@ class GalleryBlockAdapter(private val galleries: List Unit)? = null
- fun setClickListener(callback: ((Int, String) -> Unit)?) {
- this.callback = callback
- }
-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when(viewType) {
ViewType.VIEW_ITEM.ordinal -> {
@@ -70,21 +68,43 @@ class GalleryBlockAdapter(private val galleries: List View.VISIBLE
+ else -> View.GONE
+ }
+ }
+ with(galleryblock_series) {
+ text =
+ resources.getString(
+ R.string.galleryblock_series,
+ series.joinToString(", ") { it.wordCapitalize() })
+ visibility = when {
+ series.isNotEmpty() -> View.VISIBLE
+ else -> View.GONE
+ }
+ }
galleryblock_type.text = resources.getString(R.string.galleryblock_type, gallery.type).wordCapitalize()
- galleryblock_language.text =
- resources.getString(R.string.galleryblock_language, languages[gallery.language])
+ with(galleryblock_language) {
+ text =
+ resources.getString(R.string.galleryblock_language, languages[gallery.language])
+ visibility = when {
+ gallery.language.isNotEmpty() -> View.VISIBLE
+ else -> View.GONE
+ }
+ }
galleryblock_tag_group.removeAllViews()
gallery.relatedTags.forEach {
diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
similarity index 70%
rename from app/src/main/java/xyz/quaver/pupil/adapters/GalleryAdapter.kt
rename to app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
index 5d1febdc..8e84e6a6 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt
@@ -1,39 +1,31 @@
package xyz.quaver.pupil.adapters
import android.graphics.BitmapFactory
-import android.util.Log
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
+import kotlinx.android.synthetic.main.item_reader.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import xyz.quaver.pupil.R
-class GalleryAdapter(private val images: List) : RecyclerView.Adapter() {
+class ReaderAdapter(private val images: List) : RecyclerView.Adapter() {
- class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
-
- private var onClick: (() -> Unit)? = null
- fun setOnClick(callback: (() -> Unit)?) {
- this.onClick = callback
- }
+ class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
LayoutInflater.from(parent.context).inflate(
- R.layout.item_gallery, parent, false
+ R.layout.item_reader, parent, false
).let {
- return ViewHolder(it as ImageView)
+ return ViewHolder(it)
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- with(holder.view) {
- setOnClickListener {
- onClick?.invoke()
- }
-
+ with(holder.view as ImageView) {
CoroutineScope(Dispatchers.Default).launch {
val options = BitmapFactory.Options()
diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
new file mode 100644
index 00000000..7abc6c97
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
@@ -0,0 +1,96 @@
+package xyz.quaver.pupil.types
+
+data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) {
+ companion object {
+ fun parseTag(tag: String) : Tag {
+ if (tag.first() == '-') {
+ tag.substring(1).split(Regex(":"), 2).let {
+ return when(it.size) {
+ 2 -> Tag(it[0], it[1], true)
+ else -> Tag(null, tag, true)
+ }
+ }
+ }
+ tag.split(Regex(":"), 2).let {
+ return when(it.size) {
+ 2 -> Tag(it[0], it[1])
+ else -> Tag(null, tag)
+ }
+ }
+ }
+ }
+
+ override fun toString(): String {
+ return (if (isNegative) "-" else "") + when(area) {
+ null -> tag
+ else -> "$area:$tag"
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is Tag)
+ return false
+
+ if (other.area == area && other.tag == tag)
+ return true
+
+ return false
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode()
+ }
+}
+
+class Tags(tag: List?) : ArrayList() {
+
+ companion object {
+ fun parse(tags: String) : Tags {
+ return Tags(
+ tags.split(' ').map {
+ if (it.isNotEmpty())
+ Tag.parseTag(it)
+ else
+ null
+ }
+ )
+ }
+ }
+
+ init {
+ tag?.forEach {
+ if (it != null)
+ add(it)
+ }
+ }
+
+ fun contains(element: String): Boolean {
+ forEach {
+ if (it.toString() == element)
+ return true
+ }
+
+ return false
+ }
+
+ fun add(element: String): Boolean {
+ return super.add(Tag.parseTag(element))
+ }
+
+ fun remove(element: String) {
+ filter { it.toString() == element }.forEach {
+ remove(it)
+ }
+ }
+
+ fun removeByArea(area: String) {
+ filter { it.area == area }.forEach {
+ remove(it)
+ }
+ }
+
+ override fun toString(): String {
+ return joinToString(" ") { it.toString() }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/ItemClickSupport.java b/app/src/main/java/xyz/quaver/pupil/util/ItemClickSupport.java
new file mode 100644
index 00000000..765ace41
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/util/ItemClickSupport.java
@@ -0,0 +1,107 @@
+package xyz.quaver.pupil.util;
+
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import xyz.quaver.pupil.R;
+
+/*
+ Source: http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
+ USAGE:
+
+ ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
+ @Override
+ public void onItemClicked(RecyclerView recyclerView, int position, View v) {
+ // do it
+ }
+ });
+
+*/
+public class ItemClickSupport {
+ private final RecyclerView mRecyclerView;
+ private OnItemClickListener mOnItemClickListener;
+ private OnItemLongClickListener mOnItemLongClickListener;
+ private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mOnItemClickListener != null) {
+ RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
+ mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
+ }
+ }
+ };
+ private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mOnItemLongClickListener != null) {
+ RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
+ return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
+ }
+ return false;
+ }
+ };
+ private RecyclerView.OnChildAttachStateChangeListener mAttachListener
+ = new RecyclerView.OnChildAttachStateChangeListener() {
+ @Override
+ public void onChildViewAttachedToWindow(@NonNull View view) {
+ if (mOnItemClickListener != null) {
+ view.setOnClickListener(mOnClickListener);
+ }
+ if (mOnItemLongClickListener != null) {
+ view.setOnLongClickListener(mOnLongClickListener);
+ }
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(@NonNull View view) {
+
+ }
+ };
+
+ private ItemClickSupport(RecyclerView recyclerView) {
+ mRecyclerView = recyclerView;
+ mRecyclerView.setTag(R.id.item_click_support, this);
+ mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
+ }
+
+ public static ItemClickSupport addTo(RecyclerView view) {
+ ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
+ if (support == null) {
+ support = new ItemClickSupport(view);
+ }
+ return support;
+ }
+
+ public static ItemClickSupport removeFrom(RecyclerView view) {
+ ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
+ if (support != null) {
+ support.detach(view);
+ }
+ return support;
+ }
+
+ public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
+ mOnItemClickListener = listener;
+ return this;
+ }
+
+ public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
+ mOnItemLongClickListener = listener;
+ return this;
+ }
+
+ private void detach(RecyclerView view) {
+ view.removeOnChildAttachStateChangeListener(mAttachListener);
+ view.setTag(R.id.item_click_support, null);
+ }
+
+ public interface OnItemClickListener {
+
+ void onItemClicked(RecyclerView recyclerView, int position, View v);
+ }
+
+ public interface OnItemLongClickListener {
+
+ boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt
index e5037094..b25e03e0 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/update.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt
@@ -18,14 +18,5 @@ fun checkUpdate(url: String, currentVersion: String) : JsonObject? {
if (currentVersion != releases[0].jsonObject["tag_name"]?.content)
return releases[0].jsonObject
- return null
-}
-
-fun getApkUrl(releases: JsonObject, releaseName: String) : Pair? {
- releases["assets"]?.jsonArray?.forEach {
- if (Regex(releaseName).matches(it.jsonObject["name"]?.content ?: ""))
- return Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
- }
-
return null
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_email.xml b/app/src/main/res/drawable-anydpi/ic_email.xml
new file mode 100644
index 00000000..4163eb94
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_email.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_help.xml b/app/src/main/res/drawable-anydpi/ic_help.xml
new file mode 100644
index 00000000..785b2478
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_help.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/ic_email.png b/app/src/main/res/drawable-hdpi/ic_email.png
new file mode 100644
index 00000000..6cf75e2a
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_email.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_help.png b/app/src/main/res/drawable-hdpi/ic_help.png
new file mode 100644
index 00000000..64941638
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_help.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_email.png b/app/src/main/res/drawable-mdpi/ic_email.png
new file mode 100644
index 00000000..d305e584
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_email.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_help.png b/app/src/main/res/drawable-mdpi/ic_help.png
new file mode 100644
index 00000000..b2b8c636
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_help.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_email.png b/app/src/main/res/drawable-xhdpi/ic_email.png
new file mode 100644
index 00000000..6854119d
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_email.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_help.png b/app/src/main/res/drawable-xhdpi/ic_help.png
new file mode 100644
index 00000000..6ef7a89d
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_help.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_email.png b/app/src/main/res/drawable-xxhdpi/ic_email.png
new file mode 100644
index 00000000..ed17daf2
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_email.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_help.png b/app/src/main/res/drawable-xxhdpi/ic_help.png
new file mode 100644
index 00000000..760e70d4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_help.png differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 3d702355..0aec4ddd 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,7 +6,6 @@
android:id="@+id/main_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true"
tools:openDrawer="start">
diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml
index f0d84786..ffebabe9 100644
--- a/app/src/main/res/layout/activity_main_content.xml
+++ b/app/src/main/res/layout/activity_main_content.xml
@@ -22,7 +22,7 @@
+ android:layout_marginBottom="-80dp">
@@ -71,7 +71,7 @@
android:layout_height="match_parent"
app:floatingSearch_searchBarMarginLeft="8dp"
app:floatingSearch_searchBarMarginRight="8dp"
- app:floatingSearch_searchBarMarginTop="8dp"
+ app:floatingSearch_searchBarMarginTop="24dp"
app:floatingSearch_searchHint="@string/search_hint"
app:floatingSearch_suggestionsListAnimDuration="250"
app:floatingSearch_showSearchKey="true"
diff --git a/app/src/main/res/layout/activity_gallery.xml b/app/src/main/res/layout/activity_reader.xml
similarity index 63%
rename from app/src/main/res/layout/activity_gallery.xml
rename to app/src/main/res/layout/activity_reader.xml
index 34130f3e..a77f48e5 100644
--- a/app/src/main/res/layout/activity_gallery.xml
+++ b/app/src/main/res/layout/activity_reader.xml
@@ -1,18 +1,25 @@
-
+ tools:context=".ReaderActivity">
+
+
+ android:layout_height="wrap_content">
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_default_query.xml b/app/src/main/res/layout/dialog_default_query.xml
new file mode 100644
index 00000000..ca1bd29d
--- /dev/null
+++ b/app/src/main/res/layout/dialog_default_query.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_numberpicker.xml b/app/src/main/res/layout/dialog_numberpicker.xml
new file mode 100644
index 00000000..d9167359
--- /dev/null
+++ b/app/src/main/res/layout/dialog_numberpicker.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_galleryblock.xml b/app/src/main/res/layout/item_galleryblock.xml
index ce1571dd..69883a53 100644
--- a/app/src/main/res/layout/item_galleryblock.xml
+++ b/app/src/main/res/layout/item_galleryblock.xml
@@ -78,8 +78,17 @@
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
+ app:layout_constraintBottom_toTopOf="@id/galleryblock_padding"
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
+
+
diff --git a/app/src/main/res/layout/item_gallery.xml b/app/src/main/res/layout/item_reader.xml
similarity index 75%
rename from app/src/main/res/layout/item_gallery.xml
rename to app/src/main/res/layout/item_reader.xml
index 2e6f7755..9f5abe22 100644
--- a/app/src/main/res/layout/item_gallery.xml
+++ b/app/src/main/res/layout/item_reader.xml
@@ -4,6 +4,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
- android:adjustViewBounds="true"
- android:clickable="true"
- android:focusable="true"/>
\ No newline at end of file
+ android:scaleType="fitCenter"
+ android:adjustViewBounds="true"/>
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index 1877c34f..83737a33 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -12,4 +12,21 @@
android:icon="@drawable/ic_history"/>
+ -
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/reader.xml b/app/src/main/res/menu/reader.xml
new file mode 100644
index 00000000..26ae4196
--- /dev/null
+++ b/app/src/main/res/menu/reader.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index b102b437..d9519336 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -10,7 +10,7 @@
イメージキャッシュクリア
キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?
キャッシュサイズ: %1$d%2$s
- デフォルト検索キーワード
+ デフォルトキーワード
権限を拒否すると一部の機能が利用できません
一回にロードするギャラリー数
検索設定
@@ -30,4 +30,17 @@
# リリースノート(v%1$s)\n%2$s
セキュリティーモード
アプリ履歴でアプリの画面を表示しない
+ 移動
+ 非選択
+ BLフィルター
+ グロフィルター
+ "言語: "
+ デフォルトキーワード設定
+ お問い合わせ先
+ ホームページ
+ ヘルプ
+ Github
+ メールを送る
+ 準備中
+ 準備中です。
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 95f970f6..1ecefb30 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -30,4 +30,17 @@
# 릴리즈 노트(v%1$s)\n%2$s
최근 앱 목록 창에서 앱 화면을 보이지 않게 합니다
보안 모드 활성화
+ 이동
+ 미선택
+ BL물 필터
+ 고어물 필터
+ "언어: "
+ 기본 검색어 설정
+ 메일 보내기!
+ Github
+ 도움말
+ 홈페이지
+ 문의
+ 준비 중
+ 준비중입니다.\n만화 화면에서 사진을 길게 누르면 스크롤 방식이 바뀝니다. 알고 계셨나요? :)
\ No newline at end of file
diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml
index 0cce6118..1c4f0c38 100644
--- a/app/src/main/res/values-v23/styles.xml
+++ b/app/src/main/res/values-v23/styles.xml
@@ -1,9 +1,9 @@