From efefa9e174b2cdb16e67160d7f6999d61c853cff Mon Sep 17 00:00:00 2001 From: tom5079 Date: Tue, 14 May 2019 13:33:03 +0900 Subject: [PATCH] Added horizontal view for ReaderActivity Added page selector for ReaderActivity --- .idea/misc.xml | 2 +- app/build.gradle | 1 + .../java/xyz/quaver/pupil/MainActivity.kt | 79 +++++----- .../java/xyz/quaver/pupil/ReaderActivity.kt | 144 ++++++++++++++---- .../pupil/adapters/GalleryBlockAdapter.kt | 9 -- .../{GalleryAdapter.kt => ReaderAdapter.kt} | 22 +-- .../quaver/pupil/util/ItemClickSupport.java | 107 +++++++++++++ .../main/res/layout/activity_main_content.xml | 4 +- app/src/main/res/layout/activity_reader.xml | 20 +-- .../main/res/layout/dialog_numberpicker.xml | 35 +++++ .../{item_gallery.xml => item_reader.xml} | 5 +- app/src/main/res/menu/reader.xml | 9 ++ app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values/ids.xml | 4 + app/src/main/res/values/strings.xml | 5 + 16 files changed, 342 insertions(+), 106 deletions(-) rename app/src/main/java/xyz/quaver/pupil/adapters/{GalleryAdapter.kt => ReaderAdapter.kt} (70%) create mode 100644 app/src/main/java/xyz/quaver/pupil/util/ItemClickSupport.java create mode 100644 app/src/main/res/layout/dialog_numberpicker.xml rename app/src/main/res/layout/{item_gallery.xml => item_reader.xml} (75%) create mode 100644 app/src/main/res/menu/reader.xml create mode 100644 app/src/main/res/values/ids.xml 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 @@ + + + + + + + +