Compare commits

..

3 Commits
4.16 ... 4.17

Author SHA1 Message Date
Pupil
bbe29941df Merge pull request #75 from tom5079/dev
Version 4.17
2020-06-15 08:20:29 +09:00
tom5079
2720e445ea Changed low quality settting to true by default 2020-06-15 08:19:16 +09:00
tom5079
49ba579a59 Random Gallery Added
Changed tag search behavior
Loading time improved for hitomi.la
Bug fixed
2020-06-14 16:53:30 +09:00
22 changed files with 101 additions and 40 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 51 versionCode 52
versionName "4.15" versionName "4.17"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@@ -54,20 +54,19 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "androidx.biometric:biometric:1.0.1" implementation "androidx.biometric:biometric:1.0.1"
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation 'com.google.android.material:material:1.2.0-alpha05' implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation 'com.google.firebase:firebase-core:17.2.3' implementation 'com.google.firebase:firebase-core:17.4.3'
implementation 'com.google.firebase:firebase-perf:19.0.5' implementation 'com.google.firebase:firebase-perf:19.0.7'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4' implementation 'com.github.clans:fab:1.6.4'

View File

@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":51,"versionName":"4.15","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}] [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":52,"versionName":"4.17","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]

View File

@@ -59,6 +59,13 @@ class Pupil : MultiDexApplication() {
preference.edit().remove("dl_location").apply() preference.edit().remove("dl_location").apply()
} }
if (!preference.getBoolean("low_quality_reset", false)) {
preference.edit()
.putBoolean("low_quality", true)
.putBoolean("low_quality_reset", true)
.apply()
}
histories = Histories(File(ContextCompat.getDataDir(this), "histories.json")) histories = Histories(File(ContextCompat.getDataDir(this), "histories.json"))
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json")) favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))

View File

@@ -52,7 +52,7 @@ import io.fabric.sdk.android.Fabric
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main_content.* import kotlinx.android.synthetic.main.activity_main_content.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.list import kotlinx.serialization.builtins.list
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.doSearch import xyz.quaver.hitomi.doSearch
import xyz.quaver.hitomi.getGalleryIDsFromNozomi import xyz.quaver.hitomi.getGalleryIDsFromNozomi
@@ -367,6 +367,29 @@ class MainActivity : AppCompatActivity() {
} }
} }
with(main_fab_random) {
setImageResource(R.drawable.shuffle_variant)
setOnClickListener {
runBlocking {
withTimeoutOrNull(100) {
galleryIDs?.await()
}
}.let {
if (it?.isEmpty() == false) {
val galleryID = it.random()
val intent = Intent(this@MainActivity, ReaderActivity::class.java).apply {
putExtra("galleryID", galleryID)
}
startActivity(intent)
histories.add(galleryID)
}
}
}
}
with(main_fab_id) { with(main_fab_id) {
setImageResource(R.drawable.numeric) setImageResource(R.drawable.numeric)
setOnClickListener { setOnClickListener {

View File

@@ -30,8 +30,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.settings_activity.* import kotlinx.android.synthetic.main.settings_activity.*
import kotlinx.serialization.list import kotlinx.serialization.builtins.list
import kotlinx.serialization.serializer import kotlinx.serialization.builtins.serializer
import net.rdrei.android.dirchooser.DirectoryChooserActivity import net.rdrei.android.dirchooser.DirectoryChooserActivity
import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R import xyz.quaver.pupil.R

View File

@@ -26,7 +26,6 @@ import android.util.SparseArray
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.io.InputStream
import xyz.quaver.Code import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
@@ -35,10 +34,11 @@ import xyz.quaver.pupil.util.getCachedGallery
import xyz.quaver.pupil.util.getDownloadDirectory import xyz.quaver.pupil.util.getDownloadDirectory
import xyz.quaver.pupil.util.isParentOf import xyz.quaver.pupil.util.isParentOf
import xyz.quaver.pupil.util.json import xyz.quaver.pupil.util.json
import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream
import java.net.URL import java.net.URL
import java.util.concurrent.Executors
import java.util.concurrent.locks.Lock import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
@@ -245,7 +245,7 @@ class Cache(context: Context) : ContextWrapper(context) {
it.createNewFile() it.createNewFile()
} }
data.use { BufferedInputStream(data).use {
it.copyTo(FileOutputStream(cache)) it.copyTo(FileOutputStream(cache))
} }
} }

View File

@@ -46,11 +46,10 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.ui.ReaderActivity
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@UseExperimental(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class DownloadWorker private constructor(context: Context) : ContextWrapper(context) { class DownloadWorker private constructor(context: Context) : ContextWrapper(context) {
private val preferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) private val preferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
@@ -164,7 +163,10 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
.connectTimeout(0, TimeUnit.SECONDS) .connectTimeout(0, TimeUnit.SECONDS)
.addInterceptor(interceptor) .addInterceptor(interceptor)
.readTimeout(0, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.SECONDS)
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4))) .dispatcher(Dispatcher().apply {
maxRequests = 4
maxRequestsPerHost = 4
})
.proxy(proxy) .proxy(proxy)
.build() .build()
@@ -222,7 +224,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
imageUrlFromImage( imageUrlFromImage(
galleryID, galleryID,
reader.galleryInfo.files[index], reader.galleryInfo.files[index],
lowQuality !lowQuality
) )
) )
addHeader("Referer", getReferer(galleryID)) addHeader("Referer", getReferer(galleryID))
@@ -240,6 +242,8 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}.build() }.build()
client.newCall(request).enqueue(callback) client.newCall(request).enqueue(callback)
Log.i("PUPILD", "DOWNLOADING ($galleryID, $index) from ${request.url()}")
} }
private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch { private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {

View File

@@ -18,13 +18,14 @@
package xyz.quaver.pupil.util package xyz.quaver.pupil.util
import kotlinx.serialization.list import kotlinx.serialization.KSerializer
import kotlinx.serialization.serializer import kotlinx.serialization.builtins.list
import kotlinx.serialization.builtins.serializer
import java.io.File import java.io.File
class Histories(private val file: File) : ArrayList<Int>() { class Histories(private val file: File) : ArrayList<Int>() {
val serializer = Int.serializer().list val serializer: KSerializer<List<Int>> = Int.serializer().list
init { init {
if (!file.exists()) if (!file.exists())

View File

@@ -22,9 +22,7 @@ import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.builtins.list
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.list
import java.io.File import java.io.File
import java.security.MessageDigest import java.security.MessageDigest

View File

@@ -22,7 +22,7 @@ import android.annotation.SuppressLint
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@UseExperimental(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
fun String.wordCapitalize() : String { fun String.wordCapitalize() : String {
val result = ArrayList<String>() val result = ArrayList<String>()

View File

@@ -0,0 +1,8 @@
<!-- drawable/shuffle_variant.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M17,3L22.25,7.5L17,12L22.25,16.5L17,21V18H14.26L11.44,15.18L13.56,13.06L15.5,15H17V12L17,9H15.5L6.5,18H2V15H5.26L14.26,6H17V3M2,6H6.5L9.32,8.82L7.2,10.94L5.26,9H2V6Z" />
</vector>

View File

@@ -100,6 +100,13 @@
app:fab_label="@string/main_jump_title" app:fab_label="@string/main_jump_title"
app:fab_size="mini"/> app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_fab_random"
app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_id" android:id="@+id/main_fab_id"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -140,4 +140,5 @@
<string name="import_old_galleries_folder_not_readable">フォルダを読めません</string> <string name="import_old_galleries_folder_not_readable">フォルダを読めません</string>
<string name="import_old_galleries_notification">旧ギャラリーインポート中…</string> <string name="import_old_galleries_notification">旧ギャラリーインポート中…</string>
<string name="import_old_galleries_notification_done">インポート完了</string> <string name="import_old_galleries_notification_done">インポート完了</string>
<string name="main_fab_random">ランダムギャラリーを開く</string>
</resources> </resources>

View File

@@ -140,4 +140,5 @@
<string name="import_old_galleries_folder_not_readable">폴더를 읽을 수 없습니다</string> <string name="import_old_galleries_folder_not_readable">폴더를 읽을 수 없습니다</string>
<string name="import_old_galleries_notification">이전 버전 갤러리 가져오는 중…</string> <string name="import_old_galleries_notification">이전 버전 갤러리 가져오는 중…</string>
<string name="import_old_galleries_notification_done">가져오기 완료</string> <string name="import_old_galleries_notification_done">가져오기 완료</string>
<string name="main_fab_random">무작위 갤러리 열기</string>
</resources> </resources>

View File

@@ -69,6 +69,7 @@
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string> <string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
<string name="main_open_gallery_by_id">Open Gallery by ID</string> <string name="main_open_gallery_by_id">Open Gallery by ID</string>
<string name="reader_failed_to_find_gallery">Failed to open gallery</string> <string name="reader_failed_to_find_gallery">Failed to open gallery</string>
<string name="main_fab_random">Open a random gallery</string>
<string name="main_fab_cancel">Cancel all downloads</string> <string name="main_fab_cancel">Cancel all downloads</string>
<string name="main_move">Move to page %1$d</string> <string name="main_move">Move to page %1$d</string>

View File

@@ -50,7 +50,8 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="low_quality" app:key="low_quality"
app:title="@string/settings_low_quality" app:title="@string/settings_low_quality"
app:summary="@string/settings_low_quality_summary"/> app:summary="@string/settings_low_quality_summary"
app:defaultValue="true"/>
</PreferenceCategory> </PreferenceCategory>

View File

@@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.61' ext.kotlin_version = '1.3.72'
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url 'https://maven.fabric.io/public' } maven { url 'https://maven.fabric.io/public' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.1' classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"

View File

@@ -6,7 +6,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
implementation 'org.jsoup:jsoup:1.12.1' implementation 'org.jsoup:jsoup:1.12.1'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
} }

View File

@@ -16,10 +16,20 @@
package xyz.quaver package xyz.quaver
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json
import java.net.Proxy import java.net.Proxy
var proxy = Proxy.NO_PROXY var proxy = Proxy.NO_PROXY
@OptIn(UnstableDefault::class)
var json = Json {
isLenient = true
ignoreUnknownKeys = true
serializeSpecialFloatingPointValues = true
useArrayPolymorphism = true
}
fun availableInHiyobi(galleryID: Int) : Boolean { fun availableInHiyobi(galleryID: Int) : Boolean {
return try { return try {
xyz.quaver.hiyobi.getReader(galleryID) xyz.quaver.hiyobi.getReader(galleryID)

View File

@@ -16,7 +16,7 @@
package xyz.quaver.hitomi package xyz.quaver.hitomi
import kotlinx.serialization.json.Json import xyz.quaver.json
import xyz.quaver.proxy import xyz.quaver.proxy
import java.net.URL import java.net.URL
@@ -24,7 +24,7 @@ const val protocol = "https:"
@Suppress("EXPERIMENTAL_API_USAGE") @Suppress("EXPERIMENTAL_API_USAGE")
fun getGalleryInfo(galleryID: Int) = fun getGalleryInfo(galleryID: Int) =
Json.nonstrict.parse( json.parse(
GalleryInfo.serializer(), GalleryInfo.serializer(),
URL("$protocol//$domain/galleries/$galleryID.js").openConnection(proxy).getInputStream().use { URL("$protocol//$domain/galleries/$galleryID.js").openConnection(proxy).getInputStream().use {
it.reader().readText() it.reader().readText()

View File

@@ -39,7 +39,7 @@ fun sha256(data: ByteArray) : ByteArray {
return MessageDigest.getInstance("SHA-256").digest(data) return MessageDigest.getInstance("SHA-256").digest(data)
} }
@UseExperimental(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
fun hashTerm(term: String) : UByteArray { fun hashTerm(term: String) : UByteArray {
return sha256(term.toByteArray()).toUByteArray().sliceArray(0 until 4) return sha256(term.toByteArray()).toUByteArray().sliceArray(0 until 4)
} }
@@ -258,9 +258,9 @@ fun getURLAtRange(url: String, range: LongRange) : ByteArray? {
} }
} }
@UseExperimental(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
data class Node(val keys: List<UByteArray>, val datas: List<Pair<Long, Int>>, val subNodeAddresses: List<Long>) data class Node(val keys: List<UByteArray>, val datas: List<Pair<Long, Int>>, val subNodeAddresses: List<Long>)
@UseExperimental(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
fun decodeNode(data: ByteArray) : Node { fun decodeNode(data: ByteArray) : Node {
val buffer = ByteBuffer val buffer = ByteBuffer
.wrap(data) .wrap(data)
@@ -302,7 +302,7 @@ fun decodeNode(data: ByteArray) : Node {
return Node(keys, datas, subNodeAddresses) return Node(keys, datas, subNodeAddresses)
} }
@UseExperimental(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
fun bSearch(field: String, key: UByteArray, node: Node) : Pair<Long, Int>? { fun bSearch(field: String, key: UByteArray, node: Node) : Pair<Long, Int>? {
fun compareArrayBuffers(dv1: UByteArray, dv2: UByteArray) : Int { fun compareArrayBuffers(dv1: UByteArray, dv2: UByteArray) : Int {
val top = Math.min(dv1.size, dv2.size) val top = Math.min(dv1.size, dv2.size)

View File

@@ -17,14 +17,14 @@
package xyz.quaver.hiyobi package xyz.quaver.hiyobi
import kotlinx.serialization.UnstableDefault import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json import kotlinx.serialization.builtins.list
import kotlinx.serialization.list
import org.jsoup.Jsoup import org.jsoup.Jsoup
import xyz.quaver.Code import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryFiles import xyz.quaver.hitomi.GalleryFiles
import xyz.quaver.hitomi.GalleryInfo import xyz.quaver.hitomi.GalleryInfo
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.protocol import xyz.quaver.hitomi.protocol
import xyz.quaver.json
import xyz.quaver.proxy import xyz.quaver.proxy
import java.net.URL import java.net.URL
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
@@ -62,14 +62,14 @@ fun renewCookie() : String {
} }
} }
@UseExperimental(UnstableDefault::class) @OptIn(UnstableDefault::class)
fun getReader(galleryID: Int) : Reader { fun getReader(galleryID: Int) : Reader {
val reader = "https://$hiyobi/reader/$galleryID" val reader = "https://$hiyobi/reader/$galleryID"
val url = "https://cdn.hiyobi.me/data/json/${galleryID}_list.json" val url = "https://cdn.hiyobi.me/data/json/${galleryID}_list.json"
val title = Jsoup.connect(reader).proxy(proxy).get().title() val title = Jsoup.connect(reader).proxy(proxy).get().title()
val galleryFiles = Json.nonstrict.parse( val galleryFiles = json.parse(
GalleryFiles.serializer().list, GalleryFiles.serializer().list,
with(URL(url).openConnection(proxy) as HttpsURLConnection) { with(URL(url).openConnection(proxy) as HttpsURLConnection) {
setRequestProperty("User-Agent", user_agent) setRequestProperty("User-Agent", user_agent)