diff --git a/.idea/misc.xml b/.idea/misc.xml
index 7631aec3..84da703c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 396af997..a0141594 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
index 490ecaac..5778f1e0 100644
--- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt
@@ -38,12 +38,10 @@ import ru.noties.markwon.Markwon
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.SetLineOverlap
-import xyz.quaver.pupil.util.checkUpdate
-import xyz.quaver.pupil.util.getApkUrl
+import xyz.quaver.pupil.util.*
import java.io.File
import java.io.FileOutputStream
+import java.lang.Exception
import java.util.*
import javax.net.ssl.HttpsURLConnection
import kotlin.collections.ArrayList
@@ -268,18 +266,7 @@ class MainActivity : AppCompatActivity() {
private fun setupRecyclerView() {
with(main_recyclerview) {
- adapter = GalleryBlockAdapter(galleries).apply {
- setClickListener { galleryID, title ->
- val intent = Intent(this@MainActivity, ReaderActivity::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) {
@@ -293,6 +280,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)
+ }
}
}
@@ -485,38 +483,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)
+ }.chunked(4).let { chunks ->
+ for (chunk in chunks)
+ chunk.map {
+ async {
+ try {
+ val galleryBlock = getGalleryBlock(it)
- val thumbnail = async {
- val cache = File(cacheDir, "imageCache/$it/thumbnail.${galleryBlock.thumbnails[0].path.split('.').last()}")
+ 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()
+ if (!cache.exists())
+ with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) {
+ if (!cache.parentFile.exists())
+ cache.parentFile.mkdirs()
- inputStream.copyTo(FileOutputStream(cache))
+ inputStream.copyTo(FileOutputStream(cache))
+ }
+
+ cache.absolutePath
}
- 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
index 15a929cd..260d8bdc 100644
--- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt
@@ -1,18 +1,22 @@
package xyz.quaver.pupil
import android.os.Bundle
-import android.util.Log
-import android.view.ContextMenu
-import android.view.View
-import android.view.WindowManager
+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.GalleryAdapter
+import xyz.quaver.pupil.adapters.ReaderAdapter
+import xyz.quaver.pupil.util.ItemClickSupport
import java.io.File
import java.io.FileOutputStream
import java.net.URL
@@ -22,12 +26,16 @@ 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 var screenMode = 0
+
+ private lateinit var snapHelper: PagerSnapHelper
+
+ private var menu: Menu? = null
override fun onCreate(savedInstanceState: Bundle?) {
- Log.d("Pupil", "Reader Opened")
super.onCreate(savedInstanceState)
window.setFlags(
@@ -41,12 +49,10 @@ class ReaderActivity : AppCompatActivity() {
galleryID = intent.getIntExtra("GALLERY_ID", 0)
CoroutineScope(Dispatchers.Unconfined).launch {
reader = async(Dispatchers.IO) {
- Log.d("Pupil", "Loading reader")
val preference = PreferenceManager.getDefaultSharedPreferences(this@ReaderActivity)
if (preference.getBoolean("use_hiyobi", false)) {
try {
xyz.quaver.hiyobi.getReader(galleryID)
- Log.d("Pupil", "Using Hiyobi.me")
} catch (e: Exception) {
getReader(galleryID)
}
@@ -55,13 +61,36 @@ class ReaderActivity : AppCompatActivity() {
}
}
+ 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()
- Log.d("Pupil", "Reader view init complete")
loadImages()
}
override fun onResume() {
- val preferences = android.preference.PreferenceManager.getDefaultSharedPreferences(this)
+ val preferences = PreferenceManager.getDefaultSharedPreferences(this)
if (preferences.getBoolean("security_mode", false))
window.setFlags(
@@ -69,37 +98,96 @@ class ReaderActivity : AppCompatActivity() {
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)
+ val dialog = AlertDialog.Builder(this).apply {
+ setView(view)
+ with(view.reader_dialog_number_picker) {
+ minValue=1
+ maxValue=gallerySize
+ value=currentPage
+ }
+ }.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()
}
- override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
- super.onCreateContextMenu(menu, v, menuInfo)
- }
-
private fun initView() {
- reader_recyclerview.adapter = GalleryAdapter(images).apply {
- setOnClick {
- val attrs = window.attributes
+ with(reader_recyclerview) {
+ adapter = ReaderAdapter(images)
- screenMode = (screenMode+1)%2
+ addOnScrollListener(object: RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
- when(screenMode) {
- 0 -> {
+ 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()
- }
- 1 -> {
+ } 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, 0)
+
+ preferences.edit().putBoolean("reader_one_by_one", !oneByOne).apply()
+
+ true
}
- window.attributes = attrs
- }
}
}
@@ -107,11 +195,8 @@ class ReaderActivity : AppCompatActivity() {
fun webpUrlFromUrl(url: URL) = URL(url.toString().replace("/galleries/", "/webp/") + ".webp")
loadJob = CoroutineScope(Dispatchers.Default).launch {
- Log.d("Pupil", "Reader Waiting for the data")
val reader = reader.await()
- Log.d("Pupil", "Reader Data recieved")
-
launch(Dispatchers.Main) {
with(reader_progressbar) {
max = reader.size
@@ -119,6 +204,9 @@ class ReaderActivity : AppCompatActivity() {
visibility = View.VISIBLE
}
+
+ gallerySize = reader.size
+ menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize"
}
reader.chunked(8).forEach { chunked ->
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 8eacdfe2..e83e4f86 100644
--- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt
+++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt
@@ -36,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 -> {
@@ -76,10 +71,6 @@ class GalleryBlockAdapter(private val galleries: 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/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/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml
index a1100d90..ffebabe9 100644
--- a/app/src/main/res/layout/activity_main_content.xml
+++ b/app/src/main/res/layout/activity_main_content.xml
@@ -50,13 +50,13 @@
android:id="@+id/main_swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginBottom="-64dp">
+ android:layout_marginBottom="-80dp">
diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml
index 567f5ca7..a77f48e5 100644
--- a/app/src/main/res/layout/activity_reader.xml
+++ b/app/src/main/res/layout/activity_reader.xml
@@ -1,15 +1,22 @@
-
+
+
+ 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_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_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/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..e28b1c7b 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -30,4 +30,5 @@
# リリースノート(v%1$s)\n%2$s
セキュリティーモード
アプリ履歴でアプリの画面を表示しない
+ 移動
\ 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..55df7bf9 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -30,4 +30,5 @@
# 릴리즈 노트(v%1$s)\n%2$s
최근 앱 목록 창에서 앱 화면을 보이지 않게 합니다
보안 모드 활성화
+ 이동
\ No newline at end of file
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 00000000..824c6e7d
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ac81600a..3233b4e4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,6 +8,9 @@
Thumbnail
Content ImageView
+ -/-
+
+ Fab
@@ -32,6 +35,8 @@
Type: %1$s
Language: %1$s
+ Go to page
+
Settings
Search Settings
Galleries per page