Compare commits

...

9 Commits

Author SHA1 Message Date
tom5079
d0ad7effa0 Updated README.md 2021-02-13 18:14:18 +09:00
tom5079
a032beecbf Merge remote-tracking branch 'origin/master' 2021-02-13 18:13:52 +09:00
tom5079
46ec9e48d9 (Android 11) Show warning when the download folder is set to app internal space 2021-02-13 18:12:45 +09:00
tom5079
26bcef1cc0 Fixed Related gallery not showing up on GalleryDialog 2021-02-13 17:51:18 +09:00
tom5079
bfb2f44f8f Fixed favorite tag duplication 2021-02-13 17:43:44 +09:00
tom5079
28b19b6774 migration bug fixed 2021-01-13 09:49:49 +09:00
tom5079
8d72f4a3aa Update README.md 2021-01-12 12:55:50 +09:00
tom5079
9c62e0399d Update README.md 2021-01-12 12:43:09 +09:00
tom5079
65ea09854e Fixed Bug occuring on Android 11 2021-01-12 12:40:21 +09:00
15 changed files with 103 additions and 67 deletions

View File

@@ -2,7 +2,7 @@
*Pupil, Hitomi.la viewer for Android* *Pupil, Hitomi.la viewer for Android*
![](https://img.shields.io/github/downloads/tom5079/Pupil/total) ![](https://img.shields.io/github/downloads/tom5079/Pupil/total)
[![](https://img.shields.io/github/downloads/tom5079/Pupil/5.1.6-hotfix7/Pupil-v5.1.6-hotfix7.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.6-hotfix7/Pupil-v5.1.6-hotfix7.apk) [![](https://img.shields.io/github/downloads/tom5079/Pupil/5.1.7-hotfix1/Pupil-v5.1.7-hotfix1.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.7-hotfix1/Pupil-v5.1.7-hotfix1.apk)
[![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v) [![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v)
# Features # Features

View File

@@ -38,7 +38,7 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 30 targetSdkVersion 30
versionCode 64 versionCode 64
versionName "5.1.7-beta1" versionName "5.1.7-hotfix1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }

View File

@@ -11,7 +11,7 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"versionCode": 64, "versionCode": 64,
"versionName": "5.1.7-beta1", "versionName": "5.1.7-hotfix1",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
] ]

View File

@@ -125,8 +125,13 @@ class Pupil : Application() {
favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse("")) favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "") searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
favoriteTags.filter { it.tag.contains('_') }.forEach {
favoriteTags.remove(it)
}
/*
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false) FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false)*/
try { try {
ProviderInstaller.installIfNeeded(this) ProviderInstaller.installIfNeeded(this)

View File

@@ -21,6 +21,7 @@ package xyz.quaver.pupil.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.text.util.Linkify import android.text.util.Linkify
@@ -31,8 +32,8 @@ import android.view.animation.DecelerateInterpolator
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -126,6 +127,21 @@ class MainActivity :
checkUpdate(this) checkUpdate(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Preferences["download_folder_ignore_warning", false] &&
ContextCompat.getExternalFilesDirs(this, null).map { Uri.fromFile(it).toString() }
.contains(Preferences["download_folder", ""])
) {
AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(R.string.unaccessible_download_folder)
.setPositiveButton(android.R.string.ok) { _, _ ->
DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog")
}.setNegativeButton(R.string.ignore) { _, _ ->
Preferences["download_folder_ignore_warning"] = true
}.show()
}
initView() initView()
} }

View File

@@ -19,25 +19,29 @@
package xyz.quaver.pupil.ui.dialog package xyz.quaver.pupil.ui.dialog
import android.app.Dialog import android.app.Dialog
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding
import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
class DefaultQueryDialog(context : Context) : AlertDialog(context) { class DefaultQueryDialog : DialogFragment() {
private val languages = context.resources.getStringArray(R.array.languages).map { private val languages: Map<String, String> by lazy {
requireContext().resources.getStringArray(R.array.languages).map {
it.split("|").let { split -> it.split("|").let { split ->
Pair(split[0], split[1]) Pair(split[0], split[1])
} }
}.toMap() }.toMap()
private val reverseLanguages = languages.entries.associate { (k, v) -> v to k } }
private val reverseLanguages: Map<String, String> by lazy {
languages.entries.associate { (k, v) -> v to k }
}
private val excludeBL = "-male:yaoi" private val excludeBL = "-male:yaoi"
private val excludeGuro = listOf("-female:guro", "-male:guro") private val excludeGuro = listOf("-female:guro", "-male:guro")
@@ -45,41 +49,8 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null
private lateinit var binding: DefaultQueryDialogBinding private var _binding: DefaultQueryDialogBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTitle(R.string.default_query_dialog_title)
binding = DefaultQueryDialogBinding.inflate(layoutInflater)
setView(binding.root)
initView()
setButton(Dialog.BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ ->
val newTags = Tags.parse(binding.edittext.text.toString())
with(binding.languageSelector) {
if (selectedItemPosition != 0)
newTags.add("language:${reverseLanguages[selectedItem]}")
}
if (binding.BLCheckbox.isChecked)
newTags.add(excludeBL)
if (binding.guroCheckbox.isChecked)
excludeGuro.forEach { tag ->
newTags.add(tag)
}
if (binding.loliCheckbox.isChecked)
excludeLoli.forEach { tag ->
newTags.add(tag)
}
onPositiveButtonClickListener?.invoke(newTags)
}
}
private fun initView() { private fun initView() {
val tags = Tags.parse( val tags = Tags.parse(
@@ -158,4 +129,43 @@ class DefaultQueryDialog(context : Context) : AlertDialog(context) {
} }
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = DefaultQueryDialogBinding.inflate(layoutInflater)
initView()
return AlertDialog.Builder(requireContext()).apply {
setTitle(R.string.default_query_dialog_title)
setView(binding.root)
setPositiveButton(android.R.string.ok) { _, _ ->
val newTags = Tags.parse(binding.edittext.text.toString())
with(binding.languageSelector) {
if (selectedItemPosition != 0)
newTags.add("language:${reverseLanguages[selectedItem]}")
}
if (binding.BLCheckbox.isChecked)
newTags.add(excludeBL)
if (binding.guroCheckbox.isChecked)
excludeGuro.forEach { tag ->
newTags.add(tag)
}
if (binding.loliCheckbox.isChecked)
excludeLoli.forEach { tag ->
newTags.add(tag)
}
onPositiveButtonClickListener?.invoke(newTags)
}
}.create()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
} }

View File

@@ -51,14 +51,11 @@ class DownloadLocationDialogFragment : DialogFragment() {
private val requestDownloadFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { private val requestDownloadFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) { if (it.resultCode == Activity.RESULT_OK) {
val activity = activity ?: return@registerForActivityResult
val context = context ?: return@registerForActivityResult val context = context ?: return@registerForActivityResult
val dialog = dialog ?: return@registerForActivityResult val dialog = dialog ?: return@registerForActivityResult
it.data?.data?.also { uri -> it.data?.data?.also { uri ->
val takeFlags: Int = val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.intent.flags and
(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
context.contentResolver.takePersistableUriPermission(uri, takeFlags) context.contentResolver.takePersistableUriPermission(uri, takeFlags)

View File

@@ -203,7 +203,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
} }
private fun addRelated(gallery: Gallery) { private fun addRelated(gallery: Gallery) {
val galleries = ArrayList<Int>() val galleries = mutableListOf<Int>()
val adapter = GalleryBlockAdapter(galleries).apply { val adapter = GalleryBlockAdapter(galleries).apply {
onChipClickedHandler.add { tag -> onChipClickedHandler.add { tag ->
@@ -216,7 +216,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply {
type.setText(R.string.gallery_related) type.setText(R.string.gallery_related)
RecyclerView(context).apply { contents.addView(RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
this.adapter = adapter this.adapter = adapter
@@ -236,7 +236,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
true true
} }
} }
} })
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
gallery.related.forEach { galleryID -> gallery.related.forEach { galleryID ->

View File

@@ -85,12 +85,12 @@ class SettingsFragment :
DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog") DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
} }
"default_query" -> { "default_query" -> {
DefaultQueryDialog(requireContext()).apply { DefaultQueryDialog().apply {
onPositiveButtonClickListener = { newTags -> onPositiveButtonClickListener = { newTags ->
Preferences["default_query"] = newTags.toString() Preferences["default_query"] = newTags.toString()
summary = newTags.toString() summary = newTags.toString()
} }
}.show() }.show(parentFragmentManager, "Default Query Dialog")
} }
"app_lock" -> { "app_lock" -> {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {

View File

@@ -25,6 +25,7 @@ import android.graphics.drawable.Animatable
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
@@ -110,7 +111,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
) { ) {
when(item) { when(item) {
is TagSuggestion -> { is TagSuggestion -> {
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}" val tag = "${item.n}:${item.s}"
leftIcon?.setImageDrawable( leftIcon?.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(

View File

@@ -137,5 +137,9 @@ operator fun JsonElement.get(index: Int) =
operator fun JsonElement.get(tag: String) = operator fun JsonElement.get(tag: String) =
this.jsonObject[tag] this.jsonObject[tag]
fun JsonElement.getOrNull(tag: String) = kotlin.runCatching {
this.jsonObject.getOrDefault(tag, null)
}.getOrNull()
val JsonElement.content val JsonElement.content
get() = this.jsonPrimitive.contentOrNull get() = this.jsonPrimitive.contentOrNull

View File

@@ -32,7 +32,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -182,7 +181,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
Preferences["update_download_id"] = it Preferences["update_download_id"] = it
} }
} }
setNegativeButton(if (force) android.R.string.cancel else R.string.ignore_update) { _, _ -> setNegativeButton(if (force) android.R.string.cancel else R.string.ignore) { _, _ ->
if (!force) if (!force)
preferences.edit() preferences.edit()
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000) .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
@@ -285,11 +284,11 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it) } folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it) }
}.getOrNull() }.getOrNull()
val galleryID = metadata?.get("reader")?.get("galleryInfo")?.get("id")?.content?.toIntOrNull() val galleryID = metadata?.getOrNull("reader")?.getOrNull("galleryInfo")?.getOrNull("id")?.content?.toIntOrNull()
?: folder.name.toIntOrNull() ?: return@forEachIndexed ?: folder.name.toIntOrNull() ?: return@forEachIndexed
val galleryBlock: GalleryBlock? = kotlin.runCatching { val galleryBlock: GalleryBlock? = kotlin.runCatching {
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) } metadata?.getOrNull("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
getGalleryBlock(galleryID) getGalleryBlock(galleryID)
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
@@ -297,14 +296,14 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
}.getOrNull() }.getOrNull()
val reader: Reader? = kotlin.runCatching { val reader: Reader? = kotlin.runCatching {
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) } metadata?.getOrNull("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
getReader(galleryID) getReader(galleryID)
}.getOrNull() ?: kotlin.runCatching { }.getOrNull() ?: kotlin.runCatching {
xyz.quaver.hiyobi.getReader(galleryID) xyz.quaver.hiyobi.getReader(galleryID)
}.getOrNull() }.getOrNull()
metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail -> metadata?.getOrNull("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
val file = folder.getChild(".thumbnail").also { val file = folder.getChild(".thumbnail").also {
if (it.exists()) if (it.exists())
it.delete() it.delete()

View File

@@ -73,7 +73,7 @@
<string name="main_menu_sort">ソート</string> <string name="main_menu_sort">ソート</string>
<string name="main_menu_sort_newest">投稿日時順</string> <string name="main_menu_sort_newest">投稿日時順</string>
<string name="main_menu_sort_popular">人気順</string> <string name="main_menu_sort_popular">人気順</string>
<string name="ignore_update">無視</string> <string name="ignore">無視</string>
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string> <string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string>
<string name="settings_dark_mode_title">ダークモード</string> <string name="settings_dark_mode_title">ダークモード</string>
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string> <string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
@@ -155,4 +155,5 @@
<string name="settings_tag_translation">タグ言語</string> <string name="settings_tag_translation">タグ言語</string>
<string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string> <string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string>
<string name="settings_concurrent_download">並列ダウンロード</string> <string name="settings_concurrent_download">並列ダウンロード</string>
<string name="unaccessible_download_folder">アンドロイド11以上では外部からのアプリ内部空間接近が不可能です。ダウンロードフォルダを変更しますか</string>
</resources> </resources>

View File

@@ -71,7 +71,7 @@
<string name="main_menu_sort">정렬</string> <string name="main_menu_sort">정렬</string>
<string name="main_menu_sort_popular">인기순</string> <string name="main_menu_sort_popular">인기순</string>
<string name="main_menu_sort_newest">시간순</string> <string name="main_menu_sort_newest">시간순</string>
<string name="ignore_update">무시</string> <string name="ignore">무시</string>
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string> <string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
<string name="settings_dark_mode_title">다크 모드</string> <string name="settings_dark_mode_title">다크 모드</string>
<string name="settings_dark_mode_summary">딥 다크한 모오드</string> <string name="settings_dark_mode_summary">딥 다크한 모오드</string>
@@ -155,4 +155,5 @@
<string name="settings_tag_translation">태그 언어</string> <string name="settings_tag_translation">태그 언어</string>
<string name="settings_tag_translation_message">Github에서 번역에 참여하세요</string> <string name="settings_tag_translation_message">Github에서 번역에 참여하세요</string>
<string name="settings_concurrent_download">병렬 다운로드</string> <string name="settings_concurrent_download">병렬 다운로드</string>
<string name="unaccessible_download_folder">안드로이드 11 이상에서는 외부에서 현재 다운로드 폴더에 접근할 수 없습니다. 변경하시겠습니까?</string>
</resources> </resources>

View File

@@ -28,7 +28,7 @@
<string name="warning">Warning</string> <string name="warning">Warning</string>
<string name="error">Error</string> <string name="error">Error</string>
<string name="ignore_update">Ignore</string> <string name="ignore">Ignore</string>
<string name="copied_to_clipboard">Copied to clipboard</string> <string name="copied_to_clipboard">Copied to clipboard</string>
@@ -47,6 +47,8 @@
<string name="main_no_result">No result</string> <string name="main_no_result">No result</string>
<string name="unaccessible_download_folder">From Android 11 and above, current Download folder cannot be accessed by outside apps. Would you like to change the download folder?</string>
<string name="main_drawer_home">Home</string> <string name="main_drawer_home">Home</string>
<string name="main_drawer_history">History</string> <string name="main_drawer_history">History</string>
<string name="main_drawer_downloads">Downloads</string> <string name="main_drawer_downloads">Downloads</string>