Compare commits

..

2 Commits

Author SHA1 Message Date
tom5079
678a8f0914 Merge pull request #21 from tom5079/development
Fixed bug with missing hash
2019-11-02 21:46:11 +09:00
tom5079
f2a2656837 Merge pull request #20 from tom5079/development
Development
2019-11-02 20:30:04 +09:00
26 changed files with 72 additions and 322 deletions

View File

@@ -13,8 +13,8 @@ android {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 31 versionCode 30
versionName "4.2-beta2-hotfix1" versionName "4.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@@ -25,7 +25,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
buildTypes.each { buildTypes.each {
it.buildConfigField('boolean', 'PRERELEASE', 'true') it.buildConfigField('boolean', 'PRERELEASE', 'false')
it.buildConfigField('boolean', 'CENSOR', 'false') it.buildConfigField('boolean', 'CENSOR', 'false')
} }
} }
@@ -46,7 +46,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' 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-coroutines-android:1.2.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.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.0'
@@ -55,12 +55,12 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "androidx.biometric:biometric:1.0.0" implementation "androidx.biometric:biometric:1.0.0-rc02"
implementation 'com.android.support:multidex:1.0.3' implementation 'com.android.support:multidex:1.0.3'
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-alpha02' implementation 'com.google.android.material:material:1.2.0-alpha01'
implementation 'com.google.firebase:firebase-core:17.2.1' implementation 'com.google.firebase:firebase-core:17.2.1'
implementation 'com.google.firebase:firebase-perf:19.0.3' implementation 'com.google.firebase:firebase-perf:19.0.1'
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":31,"versionName":"4.2-beta2-hotfix1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":30,"versionName":"4.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

View File

@@ -25,10 +25,6 @@ import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.json.JsonObject
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -37,9 +33,6 @@ import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.getReader import xyz.quaver.hiyobi.getReader
import xyz.quaver.hiyobi.user_agent import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.ui.LockActivity import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.util.getDownloadDirectory
import xyz.quaver.pupil.util.updateOldReaderGalleries
import java.io.File
import java.net.URL import java.net.URL
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
@@ -55,7 +48,6 @@ class ExampleInstrumentedTest {
fun useAppContext() { fun useAppContext() {
// Context of the app under test. // Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Log.i("PUPILD", getDownloadDirectory(appContext).absolutePath ?: "")
assertEquals("xyz.quaver.pupil", appContext.packageName) assertEquals("xyz.quaver.pupil", appContext.packageName)
} }
@@ -65,11 +57,13 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext val appContext = InstrumentationRegistry.getInstrumentation().targetContext
activityTestRule.launchActivity(Intent()) activityTestRule.launchActivity(Intent())
while(true);
} }
@Test @Test
fun test_doSearch() { fun test_doSearch() {
val reader = getReader( 1426382) val reader = getReader(1426382)
val data: ByteArray val data: ByteArray
@@ -82,38 +76,4 @@ class ExampleInstrumentedTest {
Log.d("Pupil", data.size.toString()) Log.d("Pupil", data.size.toString())
} }
}
@UseExperimental(ImplicitReflectionSerializer::class)
@Test
fun test_deleteCodeFromReader() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val json = Json(JsonConfiguration.Stable)
listOf(
getDownloadDirectory(context),
File(context.cacheDir, "imageCache")
).forEach { root ->
root.listFiles()?.forEach gallery@{ gallery ->
val reader = json.parseJson(File(gallery, "reader.json").apply {
if (!exists())
return@gallery
}.readText())
.jsonObject.toMutableMap()
Log.d("PUPILD", gallery.name)
reader.remove("code")
File(gallery, "reader.json").writeText(JsonObject(reader).toString())
}
}
}
@Test
fun test_updateOldReader() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
updateOldReaderGalleries(context)
}
}

View File

@@ -20,18 +20,6 @@
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:replace="android:theme"> tools:replace="android:theme">
<provider
android:authorities="${applicationId}.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
<activity android:name=".ui.LockActivity"/> <activity android:name=".ui.LockActivity"/>
<activity <activity
android:name=".ui.ReaderActivity" android:name=".ui.ReaderActivity"

View File

@@ -30,11 +30,7 @@ import androidx.preference.PreferenceManager
import com.google.android.gms.common.GooglePlayServicesNotAvailableException import com.google.android.gms.common.GooglePlayServicesNotAvailableException
import com.google.android.gms.common.GooglePlayServicesRepairableException import com.google.android.gms.common.GooglePlayServicesRepairableException
import com.google.android.gms.security.ProviderInstaller import com.google.android.gms.security.ProviderInstaller
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import xyz.quaver.pupil.util.Histories import xyz.quaver.pupil.util.Histories
import xyz.quaver.pupil.util.updateOldReaderGalleries
import java.io.File import java.io.File
class Pupil : MultiDexApplication() { class Pupil : MultiDexApplication() {
@@ -82,10 +78,6 @@ class Pupil : MultiDexApplication() {
false -> AppCompatDelegate.MODE_NIGHT_NO false -> AppCompatDelegate.MODE_NIGHT_NO
}) })
CoroutineScope(Dispatchers.IO).launch {
updateOldReaderGalleries(this@Pupil)
}
super.onCreate() super.onCreate()
} }

View File

@@ -337,7 +337,7 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
holder.view.galleryblock_download.text = when(GalleryDownloader.get(gallery.first.id)) { holder.view.galleryblock_download.text = when(GalleryDownloader.get(gallery.first.id)) {
null -> holder.view.context.getString(R.string.main_download) null -> holder.view.context.getString(R.string.main_download)
else -> holder.view.context.getString(android.R.string.cancel) else -> holder.view.context.getString(R.string.main_cancel_download)
} }
} }

View File

@@ -49,17 +49,12 @@ class ReaderAdapter(private val glide: RequestManager,
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.view as ImageView holder.view as ImageView
if (isFullScreen)
holder.view.layoutParams.height = RecyclerView.LayoutParams.MATCH_PARENT
else
holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT
glide glide
.load(File(getCachedGallery(holder.view.context, galleryID), images[position])) .load(File(getCachedGallery(holder.view.context, galleryID), images[position]))
.dontTransform()
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true) .skipMemoryCache(true)
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.dontTransform()
.apply { .apply {
if (BuildConfig.CENSOR) if (BuildConfig.CENSOR)
override(5, 8) override(5, 8)

View File

@@ -576,17 +576,6 @@ class MainActivity : AppCompatActivity() {
} }
} }
histories.remove(galleryID)
if (this@MainActivity.mode == Mode.HISTORY) {
runOnUiThread {
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
}
completeFlag.put(galleryID, false) completeFlag.put(galleryID, false)
} }

View File

@@ -32,15 +32,10 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.dialog_default_query.view.* import kotlinx.android.synthetic.main.dialog_default_query.view.*
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.parseList
import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.types.Tags
@@ -48,13 +43,10 @@ import xyz.quaver.pupil.util.Lock
import xyz.quaver.pupil.util.LockManager import xyz.quaver.pupil.util.LockManager
import xyz.quaver.pupil.util.getDownloadDirectory import xyz.quaver.pupil.util.getDownloadDirectory
import java.io.File import java.io.File
import java.nio.charset.Charset
import java.util.*
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
val REQUEST_LOCK = 38238 val REQUEST_LOCK = 38238
val REQUEST_RESTORE = 16546
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -162,7 +154,7 @@ class SettingsActivity : AppCompatActivity() {
with(findPreference<Preference>("delete_downloads")) { with(findPreference<Preference>("delete_downloads")) {
this!! this!!
val dir = getDownloadDirectory(context) val dir = getDownloadDirectory(context)!!
summary = getDirSize(dir) summary = getDirSize(dir)
@@ -286,7 +278,7 @@ class SettingsActivity : AppCompatActivity() {
s ?: return s ?: return
if (s.any { it.isUpperCase() }) if (s.any { it.isUpperCase() })
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault())) s.replace(0, s.length, s.toString().toLowerCase())
} }
}) })
} }
@@ -360,37 +352,6 @@ class SettingsActivity : AppCompatActivity() {
true true
} }
} }
with(findPreference<Preference>("backup")) {
this!!
onPreferenceClickListener = Preference.OnPreferenceClickListener {
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
File(getDownloadDirectory(context), "favorites.json"),
true
)
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
.show()
true
}
}
with(findPreference<Preference>("restore")) {
this!!
onPreferenceClickListener = Preference.OnPreferenceClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
activity?.startActivityForResult(intent, (activity as SettingsActivity).REQUEST_RESTORE)
true
}
}
} }
} }
@@ -454,7 +415,6 @@ class SettingsActivity : AppCompatActivity() {
return true return true
} }
@UseExperimental(ImplicitReflectionSerializer::class)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) { when(requestCode) {
REQUEST_LOCK -> { REQUEST_LOCK -> {
@@ -466,33 +426,6 @@ class SettingsActivity : AppCompatActivity() {
.commitAllowingStateLoss() .commitAllowingStateLoss()
} }
} }
REQUEST_RESTORE -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data ?: return
try {
val json = contentResolver.openInputStream(uri).use { inputStream ->
inputStream!!
inputStream.readBytes().toString(Charset.defaultCharset())
}
(application as Pupil).favorites.addAll(Json.parseList<Int>(json).also {
Snackbar.make(
window.decorView,
getString(R.string.settings_restore_successful, it.size),
Snackbar.LENGTH_LONG
).show()
})
} catch (e: Exception) {
Snackbar.make(
window.decorView,
R.string.settings_restore_failed,
Snackbar.LENGTH_LONG
).show()
}
}
}
else -> super.onActivityResult(requestCode, resultCode, data) else -> super.onActivityResult(requestCode, resultCode, data)
} }
} }

View File

@@ -31,6 +31,7 @@ import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonConfiguration
import xyz.quaver.availableInHiyobi
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
@@ -123,7 +124,7 @@ class GalleryDownloader(
val cached = json.parse(serializer, cache.readText()) val cached = json.parse(serializer, cache.readText())
if (cached.galleryInfo.isNotEmpty()) { if (cached.galleryInfo.isNotEmpty()) {
useHiyobi = cached.code == Reader.Code.HIYOBI useHiyobi = availableInHiyobi(galleryID)
onReaderLoadedHandler?.invoke(cached) onReaderLoadedHandler?.invoke(cached)
@@ -186,11 +187,15 @@ class GalleryDownloader(
async(Dispatchers.IO) { async(Dispatchers.IO) {
val url = when(useHiyobi) { val url = when(useHiyobi) {
true -> createImgList(galleryID, reader)[index].path true -> createImgList(galleryID, reader)[index].path
false -> when { false -> when (galleryInfo.haswebp) {
(!galleryInfo.hash.isNullOrBlank()) and (galleryInfo.haswebp == 1) -> 1 -> webpUrlFromUrl(
urlFromUrlFromHash(galleryID, galleryInfo, "webp") urlFromUrlFromHash(
else -> galleryID,
urlFromUrlFromHash(galleryID, galleryInfo) galleryInfo,
true
)
)
else -> urlFromUrlFromHash(galleryID, galleryInfo)
} }
} }

View File

@@ -33,9 +33,9 @@ fun getCachedGallery(context: Context, galleryID: Int): File {
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun getDownloadDirectory(context: Context): File { fun getDownloadDirectory(context: Context): File? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
context.getExternalFilesDir("Pupil")!! context.getExternalFilesDir("Pupil")
else else
File(Environment.getExternalStorageDirectory(), "Pupil") File(Environment.getExternalStorageDirectory(), "Pupil")
} }

View File

@@ -18,14 +18,8 @@
package xyz.quaver.pupil.util package xyz.quaver.pupil.util
import android.content.Context
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.internal.EnumSerializer
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import xyz.quaver.availableInHiyobi
import xyz.quaver.hitomi.Reader
import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.BuildConfig
import java.io.File
import java.net.URL import java.net.URL
fun getReleases(url: String) : JsonArray { fun getReleases(url: String) : JsonArray {
@@ -67,59 +61,4 @@ fun getApkUrl(releases: JsonObject) : Pair<String?, String?>? {
else else
Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content) Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
} }
}
fun getOldReaderGalleries(context: Context) : List<File> {
val oldGallery = mutableListOf<File>()
listOf(
getDownloadDirectory(context),
File(context.cacheDir, "imageCache")
).forEach { root ->
root.listFiles()?.forEach { gallery ->
File(gallery, "reader.json").let { readerFile ->
if (!readerFile.exists())
return@let
try {
Json(JsonConfiguration.Stable).parseJson(readerFile.readText())
.jsonObject.let { reader ->
if (!reader.contains("code"))
oldGallery.add(gallery)
}
} catch (e: Exception) {
// do nothing
}
}
}
}
return oldGallery
}
@UseExperimental(InternalSerializationApi::class)
fun updateOldReaderGalleries(context: Context) {
val json = Json(JsonConfiguration.Stable)
getOldReaderGalleries(context).forEach { gallery ->
val reader = json.parseJson(File(gallery, "reader.json").apply {
if (!exists())
return@forEach
}.readText())
.jsonObject.toMutableMap()
val codeSerializer = EnumSerializer(Reader.Code::class)
reader["code"] = when {
(File(gallery, "images").list()?.
all { !it.endsWith("webp") } ?: return@forEach) &&
availableInHiyobi(gallery.name.toIntOrNull() ?: return@forEach)
-> json.toJson(codeSerializer, Reader.Code.HIYOBI)
else -> json.toJson(codeSerializer, Reader.Code.HITOMI)
}
File(gallery, "reader.json").writeText(JsonObject(reader).toString())
}
} }

View File

@@ -26,11 +26,18 @@
android:background="@color/dark_gray" android:background="@color/dark_gray"
tools:context=".ui.ReaderActivity"> tools:context=".ui.ReaderActivity">
<androidx.recyclerview.widget.RecyclerView <LinearLayout
android:id="@+id/reader_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> android:layout_gravity="center_vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reader_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -22,4 +22,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="100dp" android:minHeight="100dp"
android:paddingBottom="8dp"/> android:paddingBottom="8dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>

View File

@@ -104,10 +104,5 @@
<string name="reader_help">ヘルプ</string> <string name="reader_help">ヘルプ</string>
<string name="main_delete">削除</string> <string name="main_delete">削除</string>
<string name="main_download">ダウンロード</string> <string name="main_download">ダウンロード</string>
<string name="settings_backup_title">お気に入りバックアップ</string> <string name="main_cancel_download">キャンセル</string>
<string name="settings_restore_title">お気に入り復元</string>
<string name="settings_backup_snackbar">バックアップファイルを作成しました</string>
<string name="settings_backup_checkout">確認</string>
<string name="settings_restore_failed">復元に失敗しました</string>
<string name="settings_restore_successful">%1$d項目を復元しました</string>
</resources> </resources>

View File

@@ -66,7 +66,7 @@
<string name="main_open_gallery_by_id">갤러리 번호로 열기</string> <string name="main_open_gallery_by_id">갤러리 번호로 열기</string>
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string> <string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
<string name="settings_storage">저장 공간</string> <string name="settings_storage">저장 공간</string>
<string name="main_drawer_grouop_contact_discord">디스코드</string> <string name="main_drawer_grouop_contact_discord">디스코드</string>
<string name="settings_app_lock">앱 잠금</string> <string name="settings_app_lock">앱 잠금</string>
<string name="settings_app_lock_type">앱 잠금 종류</string> <string name="settings_app_lock_type">앱 잠금 종류</string>
<string name="settings_app_version_title">앱 버전</string> <string name="settings_app_version_title">앱 버전</string>
@@ -104,10 +104,5 @@
<string name="reader_help">도움말</string> <string name="reader_help">도움말</string>
<string name="main_delete">삭제</string> <string name="main_delete">삭제</string>
<string name="main_download">다운로드</string> <string name="main_download">다운로드</string>
<string name="settings_backup_title">즐겨찾기 백업</string> <string name="main_cancel_download">취소</string>
<string name="settings_restore_title">즐겨찾기 복원</string>
<string name="settings_backup_snackbar">백업 파일을 생성하였습니다</string>
<string name="settings_backup_checkout">확인</string>
<string name="settings_restore_failed">복원에 실패했습니다</string>
<string name="settings_restore_successful">%1$d개 항목을 복원했습니다</string>
</resources> </resources>

View File

@@ -72,6 +72,7 @@
<string name="main_export_error">Error occurred during export</string> <string name="main_export_error">Error occurred during export</string>
<string name="main_download">DOWNLOAD</string> <string name="main_download">DOWNLOAD</string>
<string name="main_cancel_download">CANCEL</string>
<string name="main_delete">DELETE</string> <string name="main_delete">DELETE</string>
<string name="update_title">Update available</string> <string name="update_title">Update available</string>
@@ -132,12 +133,6 @@
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string> <string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
<string name="settings_nomedia_title">Hide image from gallery</string> <string name="settings_nomedia_title">Hide image from gallery</string>
<string name="settings_nomedia_summary">Hides image from gallery</string> <string name="settings_nomedia_summary">Hides image from gallery</string>
<string name="settings_backup_title">Backup favorites</string>
<string name="settings_backup_snackbar">Backup file created</string>
<string name="settings_backup_checkout">Check out</string>
<string name="settings_restore_title">Restore favorites</string>
<string name="settings_restore_failed">Restore failed</string>
<string name="settings_restore_successful">%1$d entries restored</string>
<string name="settings_lock_none">None</string> <string name="settings_lock_none">None</string>
<string name="settings_lock_pattern">Pattern</string> <string name="settings_lock_pattern">Pattern</string>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2019 tom5079
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path="/"/>
</paths>

View File

@@ -73,15 +73,6 @@
app:key="nomedia" app:key="nomedia"
app:title="@string/settings_nomedia_title" app:title="@string/settings_nomedia_title"
app:summary="@string/settings_nomedia_title"/> app:summary="@string/settings_nomedia_title"/>
<Preference
app:key="backup"
app:title="@string/settings_backup_title"/>
<Preference
app:key="restore"
app:title="@string/settings_restore_title"/>
</PreferenceCategory> </PreferenceCategory>
</androidx.preference.PreferenceScreen> </androidx.preference.PreferenceScreen>

View File

@@ -20,19 +20,22 @@
package xyz.quaver.pupil package xyz.quaver.pupil
import org.junit.Test
/** /**
* Example local unit test, which will execute on the development machine (host). * Example local unit test, which will execute on the development machine (host).
* *
* See [testing documentation](http://d.android.com/tools/testing). * See [testing documentation](http://d.android.com/tools/testing).
*/ */
import org.junit.Test
class ExampleUnitTest { class ExampleUnitTest {
@Test @Test
fun test() { fun test() {
val current = "0.1"
val latest = "0.2"
print(current < latest)
} }
} }

View File

@@ -1,18 +1,18 @@
// 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.50'
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.5.3' classpath 'com.android.tools.build:gradle:3.5.1'
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"
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
classpath 'io.fabric.tools:gradle:1.29.0' classpath 'io.fabric.tools:gradle:1.29.0'

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.2.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
implementation 'org.jsoup:jsoup:1.11.3' implementation 'org.jsoup:jsoup:1.11.3'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
} }

View File

@@ -51,7 +51,7 @@ fun subdomainFromGalleryID(g: Int) : String {
fun subdomainFromURL(url: String, base: String? = null) : String { fun subdomainFromURL(url: String, base: String? = null) : String {
var retval = "a" var retval = "a"
if (!base.isNullOrBlank()) if (!base.isNullOrEmpty())
retval = base retval = base
val r = Regex("""/galleries/\d*(\d)/""") val r = Regex("""/galleries/\d*(\d)/""")
@@ -60,7 +60,7 @@ fun subdomainFromURL(url: String, base: String? = null) : String {
if (m == null) { if (m == null) {
b = 16 b = 16
val r2 = Regex("""/[0-9a-f]/([0-9a-f]{2})/""") val r2 = Regex("""/images/[0-9a-f]/([0-9a-f]{2})/""")
m = r2.find(url) m = r2.find(url)
if (m == null) if (m == null)
return retval return retval
@@ -79,20 +79,17 @@ fun urlFromURL(url: String, base: String? = null) : String {
fun fullPathFromHash(hash: String?) : String? { fun fullPathFromHash(hash: String?) : String? {
return when { return when {
(hash?.length ?: 0) < 3 -> hash hash?.length ?: 0 < 3 -> hash
else -> hash!!.replace(Regex("^.*(..)(.)$"), "$2/$1/$hash") else -> hash!!.replace(Regex("^.*(..)(.)$"), "$2/$1/$hash")
} }
} }
fun urlFromHash(galleryID: Int, image: GalleryInfo, webp: String? = null) : String { fun urlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean) : String {
val ext = webp ?: image.name.split('.').last()
return when { return when {
image.hash.isNullOrBlank() -> oldMethod or image.hash.isNullOrEmpty() -> "$protocol//a.hitomi.la/galleries/$galleryID/${image.name}"
"$protocol//a.hitomi.la/galleries/$galleryID/${image.name}" else -> "$protocol//a.hitomi.la/images/${fullPathFromHash(image.hash)}.${image.name.split('.').last()}"
else ->
"$protocol//a.hitomi.la/${webp?:"images"}/${fullPathFromHash(image.hash)}.$ext"
} }
} }
fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, webp: String? = null) = fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, oldMethod: Boolean = false) =
urlFromURL(urlFromHash(galleryID, image, webp)) urlFromURL(urlFromHash(galleryID, image, oldMethod))

View File

@@ -26,19 +26,13 @@ fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
data class GalleryInfo( data class GalleryInfo(
val width: Int, val width: Int,
val hash: String? = null, val hash: String? = null,
val haswebp: Int = 0, val haswebp: Int,
val name: String, val name: String,
val height: Int val height: Int
) )
@Serializable @Serializable
data class Reader(val code: Code, val title: String, val galleryInfo: List<GalleryInfo>) { open class Reader(val title: String, val galleryInfo: List<GalleryInfo>)
enum class Code {
HITOMI,
HIYOBI,
SORALA
}
}
//Set header `Referer` to reader url to avoid 403 error //Set header `Referer` to reader url to avoid 403 error
fun getReader(galleryID: Int) : Reader { fun getReader(galleryID: Int) : Reader {
@@ -46,5 +40,5 @@ fun getReader(galleryID: Int) : Reader {
val doc = Jsoup.connect(readerUrl).get() val doc = Jsoup.connect(readerUrl).get()
return Reader(Reader.Code.HITOMI, doc.title(), getGalleryInfo(galleryID)) return Reader(doc.title(), getGalleryInfo(galleryID))
} }

View File

@@ -26,7 +26,7 @@ import xyz.quaver.hitomi.protocol
import java.net.URL import java.net.URL
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
const val hiyobi = "hiyobi.me" 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" 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"
var cookie: String = "" var cookie: String = ""
@@ -76,7 +76,7 @@ fun getReader(galleryID: Int) : Reader {
} }
) )
return Reader(Reader.Code.HIYOBI, title, galleryInfo) return Reader(title, galleryInfo)
} }
fun createImgList(galleryID: Int, reader: Reader) = fun createImgList(galleryID: Int, reader: Reader) =

View File

@@ -20,7 +20,6 @@ package xyz.quaver.hitomi
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import xyz.quaver.availableInHiyobi
class UnitTest { class UnitTest {
@Test @Test
@@ -87,17 +86,10 @@ class UnitTest {
@Test @Test
fun test_urlFromUrlFromHash() { fun test_urlFromUrlFromHash() {
val url = urlFromUrlFromHash(1531795, GalleryInfo( val url = urlFromUrlFromHash(1510702, GalleryInfo(
212, "719d46a7556be0d0021c5105878507129b5b3308b02cf67f18901b69dbb3b5ef", 1, "00.jpg", 300 210, "56e9e1b8bb72194777ed93fee11b06070b905039dd11348b070bcf1793aaed7b", 1, "6.jpg", 300
), "webp") ))
print(url) print(url)
} }
@Test
fun test_availableInHiyobi() {
val result = availableInHiyobi(1272781)
print(result)
}
} }