From 8a58564812fd97683b9dc91797b27c28312133e5 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 29 Aug 2019 12:10:51 +0900 Subject: [PATCH 1/3] Version 3.2 --- app/build.gradle | 4 ++-- app/release/output.json | 2 +- app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt | 2 +- app/src/main/java/xyz/quaver/pupil/util/update.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9aecd601..00058f24 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,7 @@ android { minSdkVersion 16 targetSdkVersion 29 versionCode 27 - versionName "3.2-beta2" + versionName "3.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true @@ -25,7 +25,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } buildTypes.each { - it.buildConfigField('boolean', 'PRERELEASE', 'true') + it.buildConfigField('boolean', 'PRERELEASE', 'false') it.buildConfigField('boolean', 'CENSOR', 'false') } } diff --git a/app/release/output.json b/app/release/output.json index 16d1dc23..0c2ac805 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":25,"versionName":"3.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":27,"versionName":"3.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index d88a7eef..85b29f8f 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -282,7 +282,7 @@ class ReaderActivity : AppCompatActivity() { onErrorHandler = { Snackbar .make(reader_layout, it.message ?: it.javaClass.name, Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.reader_help) { _ -> + .setAction(R.string.reader_help) { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.error_help)))) } .show() 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 6acfaf75..1c48f8e5 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -54,7 +54,7 @@ fun checkUpdate(url: String) : JsonObject? { fun getApkUrl(releases: JsonObject) : Pair? { return releases["assets"]?.jsonArray?.firstOrNull { - Regex("Pupil-v(\\d+\\.)+\\d+\\.apk").matches(it.jsonObject["name"]?.content ?: "") + Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.content ?: "") }.let { if (it == null) null From 043f7bedd82498abe4c6f2773349dacff7f9d364 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 30 Aug 2019 15:24:51 +0900 Subject: [PATCH 2/3] Added quick download/delete --- app/build.gradle | 4 +- .../pupil/adapters/GalleryBlockAdapter.kt | 55 ++- .../quaver/pupil/adapters/ReaderAdapter.kt | 28 -- .../java/xyz/quaver/pupil/ui/GalleryDialog.kt | 11 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 46 +++ app/src/main/res/layout/activity_lock.xml | 36 +- app/src/main/res/layout/item_galleryblock.xml | 326 ++++++++++-------- app/src/main/res/values-ja/strings.xml | 7 +- app/src/main/res/values-ko/strings.xml | 7 +- app/src/main/res/values/strings.xml | 4 + 10 files changed, 325 insertions(+), 199 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 00058f24..190565d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,7 +26,7 @@ android { } buildTypes.each { it.buildConfigField('boolean', 'PRERELEASE', 'false') - it.buildConfigField('boolean', 'CENSOR', 'false') + it.buildConfigField('boolean', 'CENSOR', 'true') } } kotlinOptions { @@ -56,6 +56,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.android.support:multidex:1.0.3' + implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation 'com.google.android.material:material:1.1.0-alpha09' implementation 'com.google.firebase:firebase-core:17.1.0' implementation 'com.google.firebase:firebase-perf:19.0.0' @@ -67,7 +68,6 @@ dependencies { transitive = false } implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' - implementation 'com.jsibbold:zoomage:1.3.0' implementation "ru.noties.markwon:core:${markwonVersion}" kapt 'com.github.bumptech.glide:compiler:4.9.0' testImplementation 'junit:junit:4.12' 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 415913db..41b2b8e6 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -31,6 +31,9 @@ import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.bumptech.glide.RequestManager import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.daimajia.swipe.SwipeLayout +import com.daimajia.swipe.adapters.RecyclerSwipeAdapter +import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.CoroutineScope @@ -45,6 +48,7 @@ import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tag +import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.Histories import xyz.quaver.pupil.util.getCachedGallery import xyz.quaver.pupil.util.wordCapitalize @@ -54,7 +58,7 @@ import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.concurrent.schedule -class GalleryBlockAdapter(private val glide: RequestManager, private val galleries: List>>) : RecyclerView.Adapter() { +class GalleryBlockAdapter(private val glide: RequestManager, private val galleries: List>>) : RecyclerSwipeAdapter(), SwipeAdapterInterface { enum class ViewType { NEXT, @@ -64,7 +68,7 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri private lateinit var favorites: Histories - inner class GalleryViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) { + inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) { fun bind(item: Pair>) { with(view) { val resources = context.resources @@ -276,6 +280,8 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri val completeFlag = SparseBooleanArray() val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() + var onDownloadClickedHandler: ((Int) -> Unit)? = null + var onDeleteClickedHandler: ((Int) -> Unit)? = null var showNext = false var showPrev = false @@ -301,8 +307,47 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is GalleryViewHolder) - holder.bind(galleries[position-(if (showPrev) 1 else 0)]) + if (holder is GalleryViewHolder) { + val gallery = galleries[position-(if (showPrev) 1 else 0)] + + holder.bind(gallery) + + with(holder.view.galleryblock_primary) { + setOnClickListener { + holder.view.performClick() + } + setOnLongClickListener { + holder.view.performLongClick() + } + } + + holder.view.galleryblock_download.setOnClickListener { + onDownloadClickedHandler?.invoke(position) + } + + holder.view.galleryblock_delete.setOnClickListener { + onDeleteClickedHandler?.invoke(position) + } + + mItemManger.bindView(holder.view, position) + + holder.view.galleryblock_swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener { + override fun onStartOpen(layout: SwipeLayout?) { + mItemManger.closeAllExcept(layout) + + holder.view.galleryblock_download.text = when(GalleryDownloader.get(gallery.first.id)) { + null -> holder.view.context.getString(R.string.main_download) + else -> holder.view.context.getString(R.string.main_cancel_download) + } + } + + override fun onClose(layout: SwipeLayout?) {} + override fun onHandRelease(layout: SwipeLayout?, xvel: Float, yvel: Float) {} + override fun onOpen(layout: SwipeLayout?) {} + override fun onStartClose(layout: SwipeLayout?) {} + override fun onUpdate(layout: SwipeLayout?, leftOffset: Int, topOffset: Int) {} + }) + } } override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { @@ -328,4 +373,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri else -> ViewType.GALLERY }.ordinal } + + override fun getSwipeLayoutResourceId(position: Int) = R.id.galleryblock_swipe_layout } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index 9b4781a8..10e42f86 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -18,18 +18,13 @@ package xyz.quaver.pupil.adapters -import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.RequestManager -import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.R import xyz.quaver.pupil.util.getCachedGallery @@ -40,7 +35,6 @@ class ReaderAdapter(private val glide: RequestManager, private val images: List) : RecyclerView.Adapter() { var isFullScreen = false - private var prev : Drawable? = null class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) @@ -64,29 +58,7 @@ class ReaderAdapter(private val glide: RequestManager, .apply { if (BuildConfig.CENSOR) override(5, 8) - if (isFullScreen) - placeholder(prev) } - .listener(object: RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ) = false - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - prev = resource?.constantState?.newDrawable()?.mutate() - - return false - } - }) .into(holder.view) } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt index 37d6ff9e..6accdeb9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt @@ -22,19 +22,14 @@ import android.app.Dialog import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.Gravity import android.view.LayoutInflater import android.view.View -import android.widget.ImageView -import android.widget.LinearLayout import android.widget.LinearLayout.LayoutParams import androidx.core.content.ContextCompat -import androidx.gridlayout.widget.GridLayout import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide -import com.bumptech.glide.RequestManager import com.google.android.material.chip.Chip import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.dialog_galleryblock.* @@ -45,6 +40,7 @@ import xyz.quaver.hitomi.Gallery import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.getGallery import xyz.quaver.hitomi.getGalleryBlock +import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.GalleryBlockAdapter @@ -114,7 +110,10 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte Glide.with(context) .load(gallery.thumbnails.firstOrNull()) - .into(gallery_thumbnail) + .apply { + if (BuildConfig.CENSOR) + override(5, 8) + }.into(gallery_thumbnail) addDetails(gallery) addThumbnails(gallery) diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 24fcc389..86569dba 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -535,6 +535,52 @@ class MainActivity : AppCompatActivity() { loadBlocks() } } + onDownloadClickedHandler = { position -> + val galleryID = galleries[position].first.id + + if (!completeFlag.get(galleryID, false)) { + val downloader = GalleryDownloader.get(galleryID) + + if (downloader == null) + GalleryDownloader(context, galleryID, true).start() + else { + downloader.cancel() + downloader.clearNotification() + } + } + + closeAllItems() + } + + onDeleteClickedHandler = { position -> + val galleryID = galleries[position].first.id + + CoroutineScope(Dispatchers.Default).launch { + with(GalleryDownloader[galleryID]) { + this?.cancelAndJoin() + this?.clearNotification() + } + val cache = File(cacheDir, "imageCache/${galleryID}") + val data = getCachedGallery(context, galleryID) + cache.deleteRecursively() + data.deleteRecursively() + + downloads.remove(galleryID) + + if (this@MainActivity.mode == Mode.DOWNLOAD) { + runOnUiThread { + cancelFetch() + clearGalleries() + fetchGalleries(query, sortMode) + loadBlocks() + } + } + + completeFlag.put(galleryID, false) + } + + closeAllItems() + } } ItemClickSupport.addTo(this) .setOnItemClickListener { _, position, v -> diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index 4aab2840..00cd2898 100644 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -29,7 +29,29 @@ android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/lock_button_layout"/> + app:layout_constraintBottom_toTopOf="@id/lock_fingerprint_layout"/> + + + + + + @@ -59,16 +81,6 @@ app:backgroundTint="@color/dark_gray" app:fabSize="mini"/> - - + android:clipChildren="true"> - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:drag_edge="right" + app:show_mode="pull_out"> + android:id="@+id/galleryblock_secondary" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + android:layout_height="match_parent" + android:padding="8dp" + android:gravity="center" + android:background="@android:color/holo_blue_dark" + android:textColor="@android:color/white" + android:text="@string/main_download" + android:foreground="?attr/selectableItemBackground" + android:focusable="true" + android:clickable="true"/> - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 f9356dd4..cd3270c6 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -99,7 +99,10 @@ タグ サムネイル おすすめ - イメージを隠す - イメージをギャラリーから見えなくする + イメージをギャラリーから見えなくする + イメージを隠す ヘルプ + 削除 + ダウンロード + キャンセル \ 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 6b01b3ee..7a261d1f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -99,7 +99,10 @@ 태그 관련 갤러리 미리보기 - 이미지 숨기기 - 갤러리에서 이미지 검색이 되지 않도록 합니다 + 갤러리에서 이미지 검색이 되지 않도록 합니다 + 이미지 숨기기 도움말 + 삭제 + 다운로드 + 취소 \ 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 40eb0bf9..e444f882 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -71,6 +71,10 @@ Open Folder Error occurred during export + DOWNLOAD + CANCEL + DELETE + Update available Download started Downloading apk… From 3b682667e12c7b4ea13bf4bfe5418b744993db63 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sat, 2 Nov 2019 20:25:03 +0900 Subject: [PATCH 3/3] Fixed bug caused by updated hitomi server structure Version 4.0 --- .idea/codeStyles/Project.xml | 12 ++++ app/build.gradle | 21 +++---- app/release/output.json | 2 +- .../quaver/pupil/ExampleInstrumentedTest.kt | 7 +-- app/src/main/AndroidManifest.xml | 5 +- .../pupil/adapters/GalleryBlockAdapter.kt | 4 +- .../java/xyz/quaver/pupil/ui/GalleryDialog.kt | 4 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 2 +- .../quaver/pupil/ui/PatternLockFragment.kt | 2 - .../xyz/quaver/pupil/ui/ReaderActivity.kt | 8 +-- .../quaver/pupil/util/GalleryDownloader.kt | 51 +++++++++------- .../main/java/xyz/quaver/pupil/util/file.kt | 2 - .../main/res/layout/dialog_galleryblock.xml | 8 +-- app/src/main/res/layout/item_galleryblock.xml | 2 + build.gradle | 8 +-- libpupil/src/main/java/xyz/quaver/Utils.kt | 26 ++++++++ .../src/main/java/xyz/quaver/hitomi/common.kt | 59 +++++++++++++++---- .../main/java/xyz/quaver/hitomi/galleries.kt | 14 ++--- .../java/xyz/quaver/hitomi/galleryblock.kt | 6 +- .../src/main/java/xyz/quaver/hitomi/reader.kt | 50 +--------------- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 26 ++++---- .../test/java/xyz/quaver/hitomi/UnitTest.kt | 17 +++++- 22 files changed, 193 insertions(+), 143 deletions(-) create mode 100644 libpupil/src/main/java/xyz/quaver/Utils.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index c33b7e4e..45b56541 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,8 @@ + + @@ -14,6 +17,7 @@ xmlns:android + ^$ @@ -24,6 +28,7 @@ xmlns:.* + ^$ @@ -35,6 +40,7 @@ .*:id + http://schemas.android.com/apk/res/android @@ -45,6 +51,7 @@ .*:name + http://schemas.android.com/apk/res/android @@ -55,6 +62,7 @@ name + ^$ @@ -65,6 +73,7 @@ style + ^$ @@ -75,6 +84,7 @@ .* + ^$ @@ -86,6 +96,7 @@ .* + http://schemas.android.com/apk/res/android @@ -97,6 +108,7 @@ .* + .* diff --git a/app/build.gradle b/app/build.gradle index 190565d8..4fd94ce0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 29 - versionCode 27 - versionName "3.2" + versionCode 29 + versionName "4.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true @@ -26,7 +26,7 @@ android { } buildTypes.each { it.buildConfigField('boolean', 'PRERELEASE', 'false') - it.buildConfigField('boolean', 'CENSOR', 'true') + it.buildConfigField('boolean', 'CENSOR', 'false') } } kotlinOptions { @@ -47,19 +47,20 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1' implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0" - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.preference:preference:1.1.0-rc01' + implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.gridlayout:gridlayout:1.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation "androidx.biometric:biometric:1.0.0-rc02" implementation 'com.android.support:multidex:1.0.3' implementation "com.daimajia.swipelayout:library:1.2.0@aar" - implementation 'com.google.android.material:material:1.1.0-alpha09' - implementation 'com.google.firebase:firebase-core:17.1.0' - implementation 'com.google.firebase:firebase-perf:19.0.0' + implementation 'com.google.android.material:material:1.2.0-alpha01' + implementation 'com.google.firebase:firebase-core:17.2.1' + implementation 'com.google.firebase:firebase-perf:19.0.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.clans:fab:1.6.4' diff --git a/app/release/output.json b/app/release/output.json index 0c2ac805..0d8ff808 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":27,"versionName":"3.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":29,"versionName":"4.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index b283f4c0..6d23423b 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -26,11 +26,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.ActivityTestRule import org.junit.Assert.assertEquals -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import xyz.quaver.hitomi.fetchNozomi import xyz.quaver.hiyobi.cookie +import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.getReader import xyz.quaver.hiyobi.user_agent import xyz.quaver.pupil.ui.LockActivity @@ -50,8 +49,6 @@ class ExampleInstrumentedTest { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext assertEquals("xyz.quaver.pupil", appContext.packageName) - - Log.d("Pupil", fetchNozomi().first.size.toString()) } @Test @@ -70,7 +67,7 @@ class ExampleInstrumentedTest { val data: ByteArray - with(URL(reader.readerItems[0].url).openConnection() as HttpsURLConnection) { + with(URL(createImgList(1426382, reader)[0].path).openConnection() as HttpsURLConnection) { setRequestProperty("User-Agent", user_agent) setRequestProperty("Cookie", cookie) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 08cc55a4..be11229f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,11 +1,13 @@ + + android:theme="@style/AppTheme" + tools:replace="android:theme"> ? + private val reader: Deferred? private var downloadJob: Job? = null private lateinit var notificationBuilder: NotificationCompat.Builder @@ -121,11 +123,8 @@ class GalleryDownloader( if (cache.exists()) { val cached = json.parse(serializer, cache.readText()) - if (cached.readerItems.isNotEmpty()) { - useHiyobi = when { - cached.readerItems[0].url.contains("hitomi.la") -> false - else -> true - } + if (cached.galleryInfo.isNotEmpty()) { + useHiyobi = availableInHiyobi(galleryID) onReaderLoadedHandler?.invoke(cached) @@ -148,7 +147,7 @@ class GalleryDownloader( } } - if (reader.readerItems.isNotEmpty()) { + if (reader.galleryInfo.isNotEmpty()) { //Save cache if (cache.parentFile?.exists() == false) cache.parentFile!!.mkdirs() @@ -159,7 +158,8 @@ class GalleryDownloader( reader } catch (e: Exception) { Crashlytics.logException(e) - Reader("", listOf()) + onErrorHandler?.invoke(e) + null } } } @@ -168,29 +168,36 @@ class GalleryDownloader( fun start() { downloadJob = CoroutineScope(Dispatchers.Default).launch { - val reader = reader!!.await() + val reader = reader!!.await() ?: return@launch notificationBuilder.setContentTitle(reader.title) - if (reader.readerItems.isEmpty()) { - onErrorHandler?.invoke(IOException(getString(R.string.unable_to_connect))) - return@launch - } - val list = ArrayList() onReaderLoadedHandler?.invoke(reader) notificationBuilder - .setProgress(reader.readerItems.size, 0, false) - .setContentText("0/${reader.readerItems.size}") + .setProgress(reader.galleryInfo.size, 0, false) + .setContentText("0/${reader.galleryInfo.size}") - reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked -> - chunked.mapIndexed { i, it -> + reader.galleryInfo.chunked(4).forEachIndexed { chunkIndex, chunked -> + chunked.mapIndexed { i, galleryInfo -> val index = chunkIndex*4+i async(Dispatchers.IO) { - val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url + val url = when(useHiyobi) { + true -> createImgList(galleryID, reader)[index].path + false -> when (galleryInfo.haswebp) { + 1 -> webpUrlFromUrl( + urlFromUrlFromHash( + galleryID, + galleryInfo, + true + ) + ) + else -> urlFromUrlFromHash(galleryID, galleryInfo) + } + } val name = "$index".padStart(4, '0') val ext = url.split('.').last() @@ -234,8 +241,8 @@ class GalleryDownloader( onProgressHandler?.invoke(index) notificationBuilder - .setProgress(reader.readerItems.size, index, false) - .setContentText("$index/${reader.readerItems.size}") + .setProgress(reader.galleryInfo.size, index, false) + .setContentText("$index/${reader.galleryInfo.size}") if (download) notificationManager.notify(galleryID, notificationBuilder.build()) diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt index 07032896..d1f3ba47 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/file.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt @@ -21,8 +21,6 @@ package xyz.quaver.pupil.util import android.content.Context import android.os.Build import android.os.Environment -import android.provider.MediaStore -import androidx.core.content.ContextCompat import java.io.File fun getCachedGallery(context: Context, galleryID: Int): File { diff --git a/app/src/main/res/layout/dialog_galleryblock.xml b/app/src/main/res/layout/dialog_galleryblock.xml index a7925e0b..793dac2c 100644 --- a/app/src/main/res/layout/dialog_galleryblock.xml +++ b/app/src/main/res/layout/dialog_galleryblock.xml @@ -40,7 +40,7 @@ android:padding="8dp"> @@ -66,7 +66,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/gallery_title" - app:layout_constraintLeft_toRightOf="@id/gallery_thumbnail" + app:layout_constraintLeft_toRightOf="@id/gallery_cover" app:layout_constraintRight_toRightOf="parent" android:layout_marginLeft="8dp" android:layout_marginStart="8dp"/> @@ -83,7 +83,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintLeft_toRightOf="@id/gallery_thumbnail" + app:layout_constraintLeft_toRightOf="@id/gallery_cover" android:layout_marginLeft="8dp" android:layout_marginStart="8dp"/> diff --git a/app/src/main/res/layout/item_galleryblock.xml b/app/src/main/res/layout/item_galleryblock.xml index 84a84d53..5d654286 100644 --- a/app/src/main/res/layout/item_galleryblock.xml +++ b/app/src/main/res/layout/item_galleryblock.xml @@ -42,6 +42,7 @@ android:id="@+id/galleryblock_download" android:layout_width="wrap_content" android:layout_height="match_parent" + android:minWidth="70dp" android:padding="8dp" android:gravity="center" android:background="@android:color/holo_blue_dark" @@ -55,6 +56,7 @@ android:id="@+id/galleryblock_delete" android:layout_width="wrap_content" android:layout_height="match_parent" + android:minWidth="70dp" android:padding="8dp" android:gravity="center" android:background="@android:color/holo_red_dark" diff --git a/build.gradle b/build.gradle index 5fcfbacf..eb4a1760 100644 --- a/build.gradle +++ b/build.gradle @@ -5,16 +5,14 @@ buildscript { repositories { google() jcenter() - maven { - url 'https://maven.fabric.io/public' - } + maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.1' + classpath 'com.google.gms:google-services:4.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath 'io.fabric.tools:gradle:1.29.0' diff --git a/libpupil/src/main/java/xyz/quaver/Utils.kt b/libpupil/src/main/java/xyz/quaver/Utils.kt new file mode 100644 index 00000000..777a1821 --- /dev/null +++ b/libpupil/src/main/java/xyz/quaver/Utils.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2019 tom5079 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.quaver + +fun availableInHiyobi(galleryID: Int) : Boolean { + return try { + xyz.quaver.hiyobi.getReader(galleryID) + true + } catch (e: Exception) { + false + } +} \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/common.kt b/libpupil/src/main/java/xyz/quaver/hitomi/common.kt index 9e35af1d..11c14bee 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/common.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/common.kt @@ -16,11 +16,25 @@ package xyz.quaver.hitomi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import kotlinx.serialization.list +import java.net.URL + const val protocol = "https:" +fun getGalleryInfo(galleryID: Int): List { + return Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, + Regex("""\[.+]""").find( + URL("$protocol//$domain/galleries/$galleryID.js").readText() + )?.value ?: "[]" + ) +} + //common.js var adapose = false -const val numberOfFrontends = 2 +const val numberOfFrontends = 3 const val domain = "ltn.hitomi.la" const val galleryblockdir = "galleryblock" const val nozomiextension = ".nozomi" @@ -37,20 +51,22 @@ fun subdomainFromGalleryID(g: Int) : String { fun subdomainFromURL(url: String, base: String? = null) : String { var retval = "a" - if (base != null) + if (!base.isNullOrEmpty()) retval = base - val r = Regex("""/\d*(\d)/""") - val m = r.find(url) + val r = Regex("""/galleries/\d*(\d)/""") + var m = r.find(url) + var b = 10 - m ?: return retval + if (m == null) { + b = 16 + val r2 = Regex("""/images/[0-9a-f]/([0-9a-f]{2})/""") + m = r2.find(url) + if (m == null) + return retval + } - var g = m.groups[1]!!.value.toIntOrNull() - - g ?: return retval - - if (g == 1) - g = 0 + val g = m.groupValues[1].toIntOrNull(b) ?: return retval retval = subdomainFromGalleryID(g) + retval @@ -58,5 +74,22 @@ fun subdomainFromURL(url: String, base: String? = null) : String { } fun urlFromURL(url: String, base: String? = null) : String { - return url.replace(Regex("//..?\\.hitomi\\.la/"), "//${subdomainFromURL(url, base)}.hitomi.la/") -} \ No newline at end of file + return url.replace(Regex("""//..?\.hitomi\.la/"""), "//${subdomainFromURL(url, base)}.hitomi.la/") +} + +fun fullPathFromHash(hash: String?) : String? { + return when { + hash?.length ?: 0 < 3 -> hash + else -> hash!!.replace(Regex("^.*(..)(.)$"), "$2/$1/$hash") + } +} + +fun urlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean) : String { + return when { + oldMethod or image.hash.isNullOrEmpty() -> "$protocol//a.hitomi.la/galleries/$galleryID/${image.name}" + else -> "$protocol//a.hitomi.la/images/${fullPathFromHash(image.hash)}.${image.name.split('.').last()}" + } +} + +fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean = false) = + urlFromURL(urlFromHash(galleryID, image, oldMethod)) \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt b/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt index 754108ce..dd52d235 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/galleries.kt @@ -17,7 +17,6 @@ package xyz.quaver.hitomi import org.jsoup.Jsoup -import java.net.URL import java.net.URLDecoder data class Gallery( @@ -35,7 +34,8 @@ data class Gallery( val thumbnails: List ) fun getGallery(galleryID: Int) : Gallery { - val url = "https://hitomi.la/galleries/$galleryID.html" + val url = Jsoup.connect("https://hitomi.la/galleries/$galleryID.html").get() + .select("a").attr("href") val doc = Jsoup.connect(url).get() @@ -46,7 +46,7 @@ fun getGallery(galleryID: Int) : Gallery { }.toList() val langList = doc.select("#lang-list a").map { - Pair(it.text(), it.attr("href").replace(".html", "")) + Pair(it.text(), "$protocol//hitomi.la${it.attr("href")}") } val cover = protocol + doc.selectFirst(".cover img").attr("src") @@ -68,11 +68,9 @@ fun getGallery(galleryID: Int) : Gallery { href.slice(5 until href.indexOf('-')) } - val thumbnails = Regex("'(//tn.hitomi.la/smalltn/\\d+/.+)',") - .findAll(doc.select("script").last().html()) - .map { - protocol + it.groups[1]!!.value - }.toList() + val thumbnails = getGalleryInfo(galleryID).map { + "$protocol//tn.hitomi.la/smalltn/$galleryID/${it.name}.jpg" + } return Gallery(related, langList, cover, title, artists, groups, type, language, series, characters, tags, thumbnails) } \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt index 4196d98e..c2e985f0 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt @@ -18,7 +18,6 @@ package xyz.quaver.hitomi import kotlinx.serialization.Serializable import org.jsoup.Jsoup -import sun.rmi.runtime.Log import java.net.URL import java.net.URLDecoder import java.nio.ByteBuffer @@ -69,6 +68,7 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String = @Serializable data class GalleryBlock( val id: Int, + val galleryUrl: String, val thumbnails: List, val title: String, val artists: List, @@ -83,6 +83,8 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? { try { val doc = Jsoup.connect(url).get() + val galleryUrl = doc.selectFirst(".lillie").attr("href") + val thumbnails = doc.select("img").map { protocol + it.attr("data-src") } val title = doc.selectFirst("h1.lillie > a").text() @@ -100,7 +102,7 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? { href.slice(5 until href.indexOf("-all")) } - return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags) + return GalleryBlock(galleryID, galleryUrl, thumbnails, title, artists, series, type, language, relatedTags) } catch (e: Exception) { return null } diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt index 1b1d6b08..89e793a9 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt @@ -17,72 +17,28 @@ package xyz.quaver.hitomi import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list import org.jsoup.Jsoup -import xyz.quaver.hiyobi.HiyobiReader -import java.net.URL fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" -fun webpReaderFromReader(reader: Reader) : Reader { - if (reader is HiyobiReader) - return reader - - return Reader(reader.title, reader.readerItems.map { - ReaderItem( - if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url, - it.galleryInfo - ) - }) -} - @Serializable data class GalleryInfo( val width: Int, + val hash: String?, val haswebp: Int, val name: String, val height: Int ) -@Serializable -data class ReaderItem( - val url: String, - val galleryInfo: GalleryInfo? -) @Serializable -open class Reader(val title: String, val readerItems: List) +open class Reader(val title: String, val galleryInfo: List) //Set header `Referer` to reader url to avoid 403 error fun getReader(galleryID: Int) : Reader { val readerUrl = "https://hitomi.la/reader/$galleryID.html" - val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" val doc = Jsoup.connect(readerUrl).get() - val title = doc.title() - - val images = doc.select(".img-url").map { - protocol + urlFromURL(it.text()) - } - - val galleryInfo = ArrayList() - - galleryInfo.addAll( - Json(JsonConfiguration.Stable).parse( - GalleryInfo.serializer().list, - Regex("""\[.+]""").find( - URL(galleryInfoUrl).readText() - )?.value ?: "[]" - ) - ) - - if (images.size > galleryInfo.size) - galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - - return Reader(title, (images zip galleryInfo).map { - ReaderItem(it.first, it.second) - }) + return Reader(doc.title(), getGalleryInfo(galleryID)) } \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index fb9a5459..4873aef1 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -18,18 +18,17 @@ package xyz.quaver.hiyobi import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.json.content +import kotlinx.serialization.list import org.jsoup.Jsoup +import xyz.quaver.hitomi.GalleryInfo import xyz.quaver.hitomi.Reader -import xyz.quaver.hitomi.ReaderItem +import xyz.quaver.hitomi.protocol 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" -class HiyobiReader(title: String, readerItems: List) : Reader(title, readerItems) - var cookie: String = "" get() { if (field.isEmpty()) @@ -38,6 +37,12 @@ var cookie: String = "" return field } +data class Images( + val path: String, + val no: Int, + val name: String +) + fun renewCookie() : String { val url = "https://$hiyobi/" @@ -59,7 +64,8 @@ fun getReader(galleryID: Int) : Reader { val title = Jsoup.connect(reader).get().title() - val json = Json(JsonConfiguration.Stable).parseJson( + val galleryInfo = Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, with(URL(url).openConnection() as HttpsURLConnection) { setRequestProperty("User-Agent", user_agent) setRequestProperty("Cookie", cookie) @@ -70,8 +76,8 @@ fun getReader(galleryID: Int) : Reader { } ) - return Reader(title, json.jsonArray.map { - val name = it.jsonObject["name"]!!.content - ReaderItem("https://$hiyobi/data/$galleryID/$name", null) - }) -} \ No newline at end of file + return Reader(title, galleryInfo) +} + +fun createImgList(galleryID: Int, reader: Reader) = + reader.galleryInfo.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) } \ No newline at end of file diff --git a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt index 79091867..b838048c 100644 --- a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt +++ b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt @@ -18,12 +18,16 @@ package xyz.quaver.hitomi +import org.junit.Assert.assertEquals import org.junit.Test class UnitTest { @Test fun test() { - + assertEquals( + "6/2d/c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6", + fullPathFromHash("c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6") + ) } @Test @@ -63,7 +67,7 @@ class UnitTest { @Test fun test_getGallery() { - val gallery = getGallery(1405267) + val gallery = getGallery(1510566) print(gallery) } @@ -77,6 +81,15 @@ class UnitTest { @Test fun test_hiyobi() { + xyz.quaver.hiyobi.getReader(1510567) + } + @Test + fun test_urlFromUrlFromHash() { + val url = urlFromUrlFromHash(1510702, GalleryInfo( + 210, "56e9e1b8bb72194777ed93fee11b06070b905039dd11348b070bcf1793aaed7b", 1, "6.jpg", 300 + )) + + print(url) } } \ No newline at end of file