Separated libpupil to standalone repository

Migrated to Kotlin 1.4
This commit is contained in:
tom5079
2020-08-23 20:26:23 +09:00
parent 735dbab695
commit 216914882c
60 changed files with 233 additions and 486 deletions

View File

@@ -2,6 +2,22 @@
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="120" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">

7
.idea/dictionaries/tom50.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="tom50">
<words>
<w>hitomi</w>
</words>
</dictionary>
</component>

1
.idea/gradle.xml generated
View File

@@ -11,7 +11,6 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/libpupil" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />

View File

@@ -58,24 +58,23 @@ android {
}
dependencies {
def markwonVersion = '3.1.0'
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "androidx.biometric:biometric:1.0.1"
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation 'com.google.android.material:material:1.3.0-alpha02'
implementation 'com.google.firebase:firebase-core:17.4.4'
implementation 'com.google.firebase:firebase-analytics:17.4.4'
implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
implementation 'com.google.firebase:firebase-core:17.5.0'
implementation 'com.google.firebase:firebase-analytics:17.5.0'
implementation 'com.google.firebase:firebase-crashlytics:17.2.1'
implementation 'com.google.firebase:firebase-perf:19.0.8'
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4'
@@ -92,15 +91,15 @@ dependencies {
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
//implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
implementation "ru.noties.markwon:core:${markwonVersion}"
implementation "ru.noties.markwon:core:3.1.0"
implementation 'xyz.quaver:libpupil:1.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(path: ':libpupil')
}
androidExtensions {
experimental = true
}
}

View File

@@ -40,9 +40,9 @@
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.SerializationKt
-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's
-keepclassmembers class xyz.quaver.** { # <-- change package name to your app's
-keepclassmembers class xyz.quaver.pupil** { # <-- change package name to your app's
*** Companion;
}
-keepclasseswithmembers class xyz.quaver.** { # <-- change package name to your app's
-keepclasseswithmembers class xyz.quaver.pupil** { # <-- change package name to your app's
kotlinx.serialization.KSerializer serializer(...);
}

View File

@@ -29,7 +29,6 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.FileProvider
import androidx.preference.PreferenceManager
import xyz.quaver.pupil.util.NOTIFICATION_ID_UPDATE
import xyz.quaver.pupil.util.cancelImport
import java.io.File
@@ -92,7 +91,7 @@ class BroadcastReciever : BroadcastReceiver() {
.setContentIntent(pendingIntent)
.build()
notificationManager.notify(NOTIFICATION_ID_UPDATE, notification)
notificationManager.notify(R.id.notification_id_update, notification)
}
ACTION_CANCEL_IMPORT -> {
cancelImport = true

View File

@@ -48,8 +48,7 @@ import kotlin.concurrent.schedule
import kotlin.math.roundToInt
class ReaderAdapter(private val glide: RequestManager,
private val galleryID: Int,
private val activity: Activity) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
private val galleryID: Int) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
var reader: Reader? = null
val timer = Timer()

View File

@@ -51,7 +51,9 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main_content.*
import kotlinx.coroutines.*
import kotlinx.serialization.builtins.list
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.doSearch
import xyz.quaver.hitomi.getGalleryIDsFromNozomi
@@ -59,7 +61,6 @@ import xyz.quaver.hitomi.getSuggestionsForQuery
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.types.TagSuggestion
import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.ui.dialog.GalleryDialog
@@ -103,9 +104,6 @@ class MainActivity : AppCompatActivity() {
private var mode = Mode.SEARCH
private var sortMode = SortMode.NEWEST
private val REQUEST_SETTINGS = 45162
private val REQUEST_LOCK = 561
private var galleryIDs: Deferred<List<Int>>? = null
private var totalItems = 0
private var loadingJob: Job? = null
@@ -132,7 +130,7 @@ class MainActivity : AppCompatActivity() {
}
if (lockManager.isNotEmpty())
startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK)
startActivityForResult(Intent(this, LockActivity::class.java), R.id.request_lock.normalizeID())
val preference = PreferenceManager.getDefaultSharedPreferences(this)
@@ -236,7 +234,7 @@ class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode) {
REQUEST_SETTINGS -> {
R.id.request_settings -> {
runOnUiThread {
cancelFetch()
clearGalleries()
@@ -244,7 +242,7 @@ class MainActivity : AppCompatActivity() {
loadBlocks()
}
}
REQUEST_LOCK -> {
R.id.request_lock -> {
if (resultCode != Activity.RESULT_OK)
finish()
}
@@ -496,10 +494,10 @@ class MainActivity : AppCompatActivity() {
closeAllItems()
}
}
ItemClickSupport.addTo(this)
.setOnItemClickListener { _, position, v ->
ItemClickSupport.addTo(this).apply {
onItemClickListener = listener@{ _, position, v ->
if (v !is CardView)
return@setOnItemClickListener
return@listener
val intent = Intent(this@MainActivity, ReaderActivity::class.java)
val gallery = galleries[position]
@@ -509,10 +507,11 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
histories.add(gallery.id)
}.setOnItemLongClickListener { _, position, v ->
}
onItemLongClickListener = listener@{ _, position, v ->
if (v !is CardView)
return@setOnItemLongClickListener true
return@listener false
val galleryID = galleries[position].id
@@ -537,6 +536,7 @@ class MainActivity : AppCompatActivity() {
true
}
}
var origin = 0f
var target = -1
@@ -766,11 +766,10 @@ class MainActivity : AppCompatActivity() {
with(main_searchview as FloatingSearchViewDayNight) {
val favoritesFile = File(ContextCompat.getDataDir(context), "favorites_tags.json")
val serializer = Tag.serializer().list
if (!favoritesFile.exists()) {
favoritesFile.createNewFile()
favoritesFile.writeText(json.stringify(serializer, Tags(listOf())))
favoritesFile.writeText(Json.encodeToString(Tags(listOf())))
}
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
@@ -785,7 +784,7 @@ class MainActivity : AppCompatActivity() {
setOnMenuItemClickListener {
when(it.itemId) {
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS)
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), R.id.request_settings.normalizeID())
R.id.main_menu_thin -> {
main_recyclerview.apply {
(adapter as GalleryBlockAdapter).apply {
@@ -832,7 +831,7 @@ class MainActivity : AppCompatActivity() {
clearSuggestions()
if (query.isEmpty() or query.endsWith(' ')) {
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
swapSuggestions(Json.decodeFromString<Tags>(favoritesFile.readText()).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
@@ -846,7 +845,7 @@ class MainActivity : AppCompatActivity() {
suggestions.filter {
val tag = "${it.n}:${it.s.replace(Regex("\\s"), "_")}"
Tags(json.parse(serializer, favoritesFile.readText())).contains(tag)
Tags(Json.decodeFromString(favoritesFile.readText())).contains(tag)
}.reversed().forEach {
suggestions.remove(it)
suggestions.add(0, it)
@@ -884,7 +883,7 @@ class MainActivity : AppCompatActivity() {
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag))
if (Tags(Json.decodeFromString(favoritesFile.readText())).contains(tag))
setImageResource(R.drawable.ic_star_filled)
else
setImageResource(R.drawable.ic_star_empty)
@@ -895,7 +894,7 @@ class MainActivity : AppCompatActivity() {
isClickable = true
setOnClickListener {
val favorites = Tags(json.parse(serializer, favoritesFile.readText()))
val favorites = Tags(Json.decodeFromString(favoritesFile.readText()))
if (favorites.contains(tag)) {
setImageResource(R.drawable.ic_star_empty)
@@ -910,7 +909,7 @@ class MainActivity : AppCompatActivity() {
favorites.add(tag)
}
favoritesFile.writeText(json.stringify(serializer, favorites))
favoritesFile.writeText(Json.encodeToString(favorites))
}
}
@@ -950,7 +949,7 @@ class MainActivity : AppCompatActivity() {
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
override fun onFocus() {
if (query.isEmpty() or query.endsWith(' '))
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
swapSuggestions(Json.decodeFromString<Tags>( favoritesFile.readText()).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
}

View File

@@ -319,7 +319,7 @@ class ReaderActivity : AppCompatActivity() {
private fun initView() {
with(reader_recyclerview) {
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, this@ReaderActivity).apply {
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID).apply {
onItemClickListener = {
if (isScroll) {
isScroll = false

View File

@@ -30,8 +30,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.settings_activity.*
import kotlinx.serialization.builtins.list
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import net.rdrei.android.dirchooser.DirectoryChooserActivity
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
@@ -80,7 +80,7 @@ class SettingsActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
REQUEST_LOCK -> {
R.id.request_lock -> {
if (resultCode == Activity.RESULT_OK) {
supportFragmentManager
.beginTransaction()
@@ -89,7 +89,7 @@ class SettingsActivity : AppCompatActivity() {
.commitAllowingStateLoss()
}
}
REQUEST_RESTORE -> {
R.id.request_restore -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data ?: return
@@ -100,7 +100,7 @@ class SettingsActivity : AppCompatActivity() {
inputStream.readBytes().toString(Charset.defaultCharset())
}
(application as Pupil).favorites.addAll(json.parse(Int.serializer().list, str).also {
(application as Pupil).favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
Snackbar.make(
window.decorView,
getString(R.string.settings_restore_successful, it.size),
@@ -116,7 +116,7 @@ class SettingsActivity : AppCompatActivity() {
}
}
}
REQUEST_DOWNLOAD_FOLDER -> {
R.id.request_download_folder -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.also { uri ->
val takeFlags: Int =
@@ -140,7 +140,7 @@ class SettingsActivity : AppCompatActivity() {
}
}
}
REQUEST_DOWNLOAD_FOLDER_OLD -> {
R.id.request_download_folder_old -> {
if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
@@ -156,7 +156,7 @@ class SettingsActivity : AppCompatActivity() {
.apply()
}
}
REQUEST_IMPORT_OLD_GALLERIES -> {
R.id.request_import_old_galleries -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.also { uri ->
val takeFlags: Int =
@@ -178,7 +178,7 @@ class SettingsActivity : AppCompatActivity() {
}
}
}
REQUEST_IMPORT_OLD_GALLERIES_OLD -> {
R.id.request_import_old_galleries_old -> {
if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
@@ -200,13 +200,13 @@ class SettingsActivity : AppCompatActivity() {
@SuppressLint("InlinedApi")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_WRITE_PERMISSION_AND_SAF -> {
R.id.request_write_permission_and_saf -> {
if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
putExtra("android.content.extra.SHOW_ADVANCED", true)
}
startActivityForResult(intent, REQUEST_DOWNLOAD_FOLDER)
startActivityForResult(intent, R.id.request_download_folder.normalizeID())
}
}
}

View File

@@ -96,13 +96,13 @@ class DownloadLocationDialog(val activity: Activity) : AlertDialog(activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_PERMISSION_AND_SAF)
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), R.id.request_write_permission_and_saf.normalizeID())
else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
putExtra("android.content.extra.SHOW_ADVANCED", true)
}
activity.startActivityForResult(intent, REQUEST_DOWNLOAD_FOLDER)
activity.startActivityForResult(intent, R.id.request_download_folder.normalizeID())
}
dismiss()
@@ -116,7 +116,7 @@ class DownloadLocationDialog(val activity: Activity) : AlertDialog(activity) {
putExtra(DirectoryChooserActivity.EXTRA_CONFIG, config)
}
activity.startActivityForResult(intent, REQUEST_DOWNLOAD_FOLDER_OLD)
activity.startActivityForResult(intent, R.id.request_download_folder_old.normalizeID())
dismiss()
}
}

View File

@@ -259,14 +259,14 @@ class GalleryDialog(context: Context, private val glide: RequestManager, private
layoutManager = LinearLayoutManager(context)
this.adapter = adapter
ItemClickSupport.addTo(this)
.setOnItemClickListener { _, position, _ ->
ItemClickSupport.addTo(this).apply {
onItemClickListener = { _, position, _ ->
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
putExtra("galleryID", galleries[position].id)
})
(context.applicationContext as Pupil).histories.add(galleries[position].id)
}
.setOnItemLongClickListener { _, position, _ ->
onItemLongClickListener = { _, position, _ ->
GalleryDialog(
context,
glide,
@@ -279,6 +279,7 @@ class GalleryDialog(context: Context, private val glide: RequestManager, private
true
}
}
}.let {
gallery_details_contents.addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
}

View File

@@ -29,11 +29,12 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.dialog_proxy.view.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import xyz.quaver.proxy
import xyz.quaver.pupil.R
import xyz.quaver.pupil.util.ProxyInfo
import xyz.quaver.pupil.util.getProxyInfo
import xyz.quaver.pupil.util.json
import java.net.Proxy
class ProxyDialog(context: Context) : Dialog(context) {
@@ -118,7 +119,7 @@ class ProxyDialog(context: Context) : Dialog(context) {
ProxyInfo(type, addr, port, username, password).let {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("proxy",
json.stringify(ProxyInfo.serializer(), it)
Json.encodeToString(it)
).apply()
proxy = it.proxy()

View File

@@ -168,7 +168,7 @@ class SettingsFragment :
}
"app_lock" -> {
val intent = Intent(requireContext(), LockActivity::class.java)
activity?.startActivityForResult(intent, REQUEST_LOCK)
activity?.startActivityForResult(intent, R.id.request_lock.normalizeID())
}
"mirrors" -> {
MirrorDialog(requireContext())
@@ -196,19 +196,19 @@ class SettingsFragment :
type = "*/*"
}
activity?.startActivityForResult(intent, REQUEST_RESTORE)
activity?.startActivityForResult(intent, R.id.request_restore.normalizeID())
}
"old_import_galleries" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_PERMISSION_AND_SAF)
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), R.id.request_write_permission_and_saf.normalizeID())
else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
putExtra("android.content.extra.SHOW_ADVANCED", true)
}
activity?.startActivityForResult(intent, REQUEST_IMPORT_OLD_GALLERIES)
activity?.startActivityForResult(intent, R.id.request_import_old_galleries.normalizeID())
}
} else { // Can't use SAF on old Androids!
val config = DirectoryChooserConfig.builder()
@@ -220,7 +220,7 @@ class SettingsFragment :
putExtra(DirectoryChooserActivity.EXTRA_CONFIG, config)
}
activity?.startActivityForResult(intent, REQUEST_IMPORT_OLD_GALLERIES_OLD)
activity?.startActivityForResult(intent, R.id.request_import_old_galleries_old.normalizeID())
}
}
"user_id" -> {

View File

@@ -1,39 +0,0 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2020 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/>.
*/
package xyz.quaver.pupil.util
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import okhttp3.Dispatcher
import okhttp3.OkHttpClient
import xyz.quaver.proxy
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
const val REQUEST_LOCK = 38238
const val REQUEST_RESTORE = 16546
const val REQUEST_IMPORT_OLD_GALLERIES = 6458
const val REQUEST_IMPORT_OLD_GALLERIES_OLD = 5946
const val REQUEST_DOWNLOAD_FOLDER = 3874
const val REQUEST_DOWNLOAD_FOLDER_OLD = 3425
const val REQUEST_WRITE_PERMISSION_AND_SAF = 13900
const val NOTIFICATION_ID_UPDATE = 2345
val json = Json(JsonConfiguration.Stable)

View File

@@ -1,107 +0,0 @@
package xyz.quaver.pupil.util;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import xyz.quaver.pupil.R;
/*
Source: http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
USAGE:
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
*/
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2020 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/>.
*/
package xyz.quaver.pupil.util
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import xyz.quaver.pupil.R
class ItemClickSupport(private val recyclerView: RecyclerView) {
var onItemClickListener: ((RecyclerView, Int, View) -> Unit)? = null
var onItemLongClickListener: ((RecyclerView, Int, View) -> Boolean)? = null
init {
recyclerView.apply {
setTag(R.id.item_click_support, this)
addOnChildAttachStateChangeListener(object: RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
onItemClickListener?.let { listener ->
view.setOnClickListener {
recyclerView.getChildViewHolder(view).let { holder ->
listener.invoke(recyclerView, holder.adapterPosition, view)
}
}
}
onItemLongClickListener?.let { listener ->
view.setOnLongClickListener {
recyclerView.getChildViewHolder(view).let { holder ->
listener.invoke(recyclerView, holder.adapterPosition, view)
}
}
}
}
override fun onChildViewDetachedFromWindow(view: View) {
// Do Nothing
}
})
}
}
fun detach() {
recyclerView.apply {
clearOnChildAttachStateChangeListeners()
setTag(R.id.item_click_support, null)
}
}
companion object {
fun addTo(view: RecyclerView) = view.let { removeFrom(it); ItemClickSupport(it) }
fun removeFrom(view: RecyclerView) = (view.tag as? ItemClickSupport)?.detach()
}
}

View File

@@ -25,6 +25,10 @@ import android.util.SparseArray
import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Dispatcher
import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader
@@ -32,15 +36,11 @@ import xyz.quaver.proxy
import xyz.quaver.pupil.util.getCachedGallery
import xyz.quaver.pupil.util.getDownloadDirectory
import xyz.quaver.pupil.util.isParentOf
import xyz.quaver.pupil.util.json
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.net.URL
import java.util.*
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
class Cache(context: Context) : ContextWrapper(context) {
@@ -49,20 +49,6 @@ class Cache(context: Context) : ContextWrapper(context) {
private val readers = SparseArray<Reader?>()
}
private val locks = SparseArray<Lock>()
private fun lock(galleryID: Int) {
synchronized(locks) {
if (locks.indexOfKey(galleryID) < 0)
locks.put(galleryID, ReentrantLock())
}
locks[galleryID].lock()
}
private fun unlock(galleryID: Int) {
locks[galleryID]?.unlock()
}
private val preference = PreferenceManager.getDefaultSharedPreferences(this)
// Search in this order
@@ -79,7 +65,7 @@ class Cache(context: Context) : ContextWrapper(context) {
return null
return try {
json.parse(Metadata.serializer(), file.readText())
Json.decodeFromString(file.readText())
} catch (e: Exception) {
//File corrupted
file.delete()
@@ -96,7 +82,7 @@ class Cache(context: Context) : ContextWrapper(context) {
it.createNewFile()
}
file.writeText(json.stringify(Metadata.serializer(), metadata))
file.writeText(Json.encodeToString(metadata))
}
suspend fun getThumbnail(galleryID: Int): String? {
@@ -134,7 +120,7 @@ class Cache(context: Context) : ContextWrapper(context) {
)
val galleryBlock = if (metadata?.galleryBlock == null) {
CoroutineScope(Dispatchers.IO).async {
withContext(Dispatchers.IO) {
var galleryBlock: GalleryBlock? = null
for (source in sources) {
@@ -149,7 +135,7 @@ class Cache(context: Context) : ContextWrapper(context) {
}
galleryBlock
}.await() ?: return null
} ?: return null
}
else
metadata.galleryBlock
@@ -175,11 +161,9 @@ class Cache(context: Context) : ContextWrapper(context) {
Code.HIYOBI to { xyz.quaver.hiyobi.getReader(galleryID) }
).let {
if (mirrors.isNotEmpty())
it.toSortedMap(
Comparator { o1, o2 ->
mirrors.indexOf(o1.name) - mirrors.indexOf(o2.name)
}
)
it.toSortedMap{ o1, o2 ->
mirrors.indexOf(o1.name) - mirrors.indexOf(o2.name)
}
else
it
}

View File

@@ -151,7 +151,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}).build()
}
val client =
val client : OkHttpClient =
OkHttpClient.Builder()
.connectTimeout(0, TimeUnit.SECONDS)
.addInterceptor(interceptor)

View File

@@ -18,15 +18,13 @@
package xyz.quaver.pupil.util
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.list
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
class Histories(private val file: File) : ArrayList<Int>() {
val serializer: KSerializer<List<Int>> = Int.serializer().list
init {
if (!file.exists())
file.parentFile?.mkdirs()
@@ -42,16 +40,13 @@ class Histories(private val file: File) : ArrayList<Int>() {
return apply {
super.clear()
super.addAll(
json.parse(
serializer,
file.bufferedReader().use { it.readText() }
)
Json.decodeFromString(file.bufferedReader().use { it.readText() })
)
}
}
fun save() {
file.writeText(json.stringify(serializer, this))
file.writeText(Json.encodeToString(toList()))
}
override fun add(element: Int): Boolean {

View File

@@ -22,7 +22,9 @@ import android.content.Context
import android.content.ContextWrapper
import androidx.core.content.ContextCompat
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.list
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
import java.security.MessageDigest
@@ -41,7 +43,7 @@ fun hashWithSalt(password: String): Pair<String, String> {
return Pair(hash(password+salt), salt)
}
val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
const val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
@Serializable
data class Lock(val type: Type, val hash: String, val salt: String) {
@@ -80,7 +82,7 @@ class LockManager(base: Context): ContextWrapper(base) {
lock.writeText("[]")
}
locks = ArrayList(json.parse(Lock.serializer().list, lock.readText()))
locks = Json.decodeFromString(lock.readText())
}
private fun save() {
@@ -89,7 +91,7 @@ class LockManager(base: Context): ContextWrapper(base) {
if (!lock.exists())
lock.createNewFile()
lock.writeText(json.stringify(Lock.serializer().list, locks?.toList() ?: listOf()))
lock.writeText(Json.encodeToString(locks?.toList() ?: listOf()))
}
fun add(lock: Lock) {

View File

@@ -51,4 +51,12 @@ fun byteToString(byte: Long, precision : Int = 1) : String {
return "%.${precision}f ${suffix[suffixIndex]}".format(size)
}
}
/*
* Convert android generated ID to requestCode
* to prevent java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode
*
* https://stackoverflow.com/questions/38072322/generate-16-bit-unique-ids-in-android-for-startactivityforresult
*/
fun Int.normalizeID() = this.and(0xFFFF)

View File

@@ -21,6 +21,8 @@ package xyz.quaver.pupil.util
import android.content.Context
import androidx.preference.PreferenceManager
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Authenticator
import okhttp3.Credentials
import java.net.InetSocketAddress
@@ -59,5 +61,5 @@ fun getProxyInfo(context: Context) =
if (it == null)
ProxyInfo(Proxy.Type.DIRECT)
else
json.parse(ProxyInfo.serializer(), it)
Json.decodeFromString(it)
}

View File

@@ -30,10 +30,8 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager
import kotlinx.coroutines.*
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.content
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.*
import okhttp3.*
import ru.noties.markwon.Markwon
import xyz.quaver.hitomi.GalleryBlock
@@ -55,7 +53,7 @@ import java.util.concurrent.TimeUnit
fun getReleases(url: String) : JsonArray {
return try {
URL(url).readText().let {
json.parse(JsonArray.serializer(), it)
Json.decodeFromString(it)
}
} catch (e: Exception) {
JsonArray(emptyList())
@@ -72,9 +70,9 @@ fun checkUpdate(context: Context, url: String) : JsonObject? {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("beta", false))
true
else
it.jsonObject["prerelease"]?.boolean == false
it.jsonObject["prerelease"]?.jsonPrimitive?.booleanOrNull == false
}?.let {
if (it.jsonObject["tag_name"]?.content == BuildConfig.VERSION_NAME)
if (it.jsonObject["tag_name"]?.jsonPrimitive?.contentOrNull == BuildConfig.VERSION_NAME)
null
else
it.jsonObject
@@ -83,13 +81,12 @@ fun checkUpdate(context: Context, url: String) : JsonObject? {
fun getApkUrl(releases: JsonObject) : String? {
return releases["assets"]?.jsonArray?.firstOrNull {
Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.content ?: "")
Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.jsonPrimitive?.contentOrNull ?: "")
}.let {
it?.jsonObject?.get("browser_download_url")?.content
it?.jsonObject?.get("browser_download_url")?.jsonPrimitive?.contentOrNull
}
}
const val UPDATE_NOTIFICATION_ID = 384823
fun checkUpdate(context: Context, force: Boolean = false) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
@@ -99,7 +96,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
return
fun extractReleaseNote(update: JsonObject, locale: Locale) : String {
val markdown = update["body"]!!.content
val markdown = update["body"]!!.jsonPrimitive.content
val target = when(locale.language) {
"ko" -> "한국어"
@@ -137,7 +134,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
}
}
return context.getString(R.string.update_release_note, update["tag_name"]?.content, result.toString())
return context.getString(R.string.update_release_note, update["tag_name"]?.jsonPrimitive?.contentOrNull, result.toString())
}
CoroutineScope(Dispatchers.Default).launch {
@@ -253,14 +250,14 @@ fun importOldGalleries(context: Context, folder: File) = CoroutineScope(Dispatch
val reader = async {
kotlin.runCatching {
json.parse(Reader.serializer(), File(gallery, "reader.json").readText())
Json.decodeFromString<Reader>(File(gallery, "reader.json").readText())
}.getOrElse {
getReader(galleryID)
}
}
val galleryBlock = async {
kotlin.runCatching {
json.parse(GalleryBlock.serializer(), File(gallery, "galleryBlock.json").readText())
Json.decodeFromString<GalleryBlock>(File(gallery, "galleryBlock.json").readText())
}.getOrElse {
getGalleryBlock(galleryID)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 B

View File

@@ -1,8 +0,0 @@
<!-- drawable/export.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="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

View File

@@ -1,8 +0,0 @@
<!-- drawable/sort_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="M3,13H15V11H3M3,6V8H21V6M3,18H9V16H3V18Z" />
</vector>

View File

@@ -17,9 +17,7 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -17,9 +17,7 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -108,7 +108,6 @@
</LinearLayout>
<LinearLayout
android:id="@+id/default_query_dialog_loli_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"

View File

@@ -18,6 +18,7 @@
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/gallery_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -47,7 +48,8 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/gallery_title"/>
app:layout_constraintRight_toLeftOf="@id/gallery_title"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/gallery_title"
@@ -72,7 +74,6 @@
android:layout_marginStart="8dp"/>
<View
android:id="@+id/gallery_padding"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/gallery_artist"

View File

@@ -1,23 +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/>.
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="3"/>

View File

@@ -20,6 +20,7 @@
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
@@ -34,8 +35,7 @@
app:show_mode="pull_out">
<LinearLayout
android:id="@+id/galleryblock_secondary"
android:layout_width="wrap_content"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
@@ -50,7 +50,8 @@
android:text="@string/main_download"
android:foreground="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"/>
android:clickable="true"
tools:ignore="UnusedAttribute" />
<TextView
android:id="@+id/galleryblock_delete"
@@ -64,7 +65,8 @@
android:text="@string/main_delete"
android:foreground="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"/>
android:clickable="true"
tools:ignore="UnusedAttribute" />
</LinearLayout>
@@ -75,7 +77,8 @@
android:orientation="vertical"
android:foreground="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true">
android:clickable="true"
tools:ignore="UnusedAttribute">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

View File

@@ -18,6 +18,7 @@
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -44,6 +45,7 @@
app:tint="?attr/colorControlNormal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,28 +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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="wrap_content">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</LinearLayout>

View File

@@ -1,35 +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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/gallery_favorite"
android:icon="@drawable/ic_star_empty"
android:title=""
app:showAsAction="ifRoom"/>
<item
android:id="@+id/gallery_download"
android:icon="@drawable/ic_download"
android:title=""
app:showAsAction="always"/>
</menu>

View File

@@ -23,9 +23,7 @@
<item android:id="@+id/main_menu_thin"
android:title="@string/main_menu_thin"/>
<item
android:id="@+id/main_menu_sort"
android:title="@string/main_menu_sort">
<item android:title="@string/main_menu_sort">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/main_menu_sort_newest"

View File

@@ -4,9 +4,7 @@
<string name="galleryblock_series">シリーズ: %1$s</string>
<string name="galleryblock_type">タイプ: %1$s</string>
<string name="main_no_result">結果なし</string>
<string name="main_search">検索</string>
<string name="search_hint">ギャラリー検索</string>
<string name="search_hint_with_page">ギャラリー検索</string>
<string name="settings_clear_cache">キャッシュクリア</string>
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string>
<string name="settings_storage_usage">%s使用中</string>
@@ -25,7 +23,6 @@
<string name="settings_clear_history_summary">履歴数: %1$d</string>
<string name="main_drawer_history">履歴</string>
<string name="main_drawer_home">トップ</string>
<string name="update_download_started">ダウンロード中</string>
<string name="update_release_note"># リリースノート(v%1$s)\n%2$s</string>
<string name="settings_security_mode_title">セキュリティーモード</string>
<string name="settings_security_mode_summary">アプリ履歴でアプリの画面を表示しない</string>
@@ -46,9 +43,7 @@
<string name="reader_fab_download">バックグラウンドダウンロード</string>
<string name="reader_notification_text">ダウンロード中…</string>
<string name="reader_notification_complete">ダウンロード完了</string>
<string name="reader_notification_error">ダウンロードエラー</string>
<string name="reader_fab_download_cancel">バックグラウンドダウンロード中止</string>
<string name="main_dialog_delete">このギャラリーを削除</string>
<string name="main_drawer_downloads">ダウンロード</string>
<string name="main_jump_title">ページ移動</string>
<string name="main_jump_message">現ページ番号: %1$d\nページ数: %2$d</string>
@@ -56,10 +51,6 @@
<string name="main_move">%1$dページへ移動</string>
<string name="https_block_alert_title">(Korean only)</string>
<string name="https_block_alert">(Korean only)</string>
<string name="main_dialog_export">ギャラリーエクスポート</string>
<string name="main_export_complete">エクスポート完了</string>
<string name="main_export_open_folder">フォルダを開く</string>
<string name="main_export_error">エクスポートエラーが発生しました</string>
<string name="settings_clear_downloads">ダウンロード削除</string>
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか</string>
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
@@ -84,11 +75,8 @@
<string name="main_menu_sort">ソート</string>
<string name="main_menu_sort_newest">投稿日時順</string>
<string name="main_menu_sort_popular">人気順</string>
<string name="update_failed">アップデートに失敗しました</string>
<string name="update_failed_message">アップデート中エラーが発生しました</string>
<string name="ignore_update">無視</string>
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string>
<string name="update_no_permission">権限がないため自動アップデートを行えません。ホームページで直接ダウンロードしてください。</string>
<string name="settings_dark_mode_title">ダークモード</string>
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
<string name="gallery_details">ギャラリー情報</string>
@@ -101,13 +89,11 @@
<string name="gallery_thumbnails">サムネイル</string>
<string name="gallery_related">おすすめ</string>
<string name="settings_nomedia_title">イメージを隠す</string>
<string name="reader_help">ヘルプ</string>
<string name="main_delete">削除</string>
<string name="main_download">ダウンロード</string>
<string name="settings_backup_title">お気に入りバックアップ</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>
<string name="settings_dl_location">ダウンロード場所</string>
@@ -134,8 +120,6 @@
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
<string name="channel_update">アップデート</string>
<string name="channel_update_description">アップデートの進行状態を表示</string>
<string name="channel_import">インポート</string>
<string name="channel_import_description">インポート状態を表示</string>
<string name="settings_import_old_galleries">旧ギャラリーインポート</string>
<string name="import_old_galleries_folder_not_readable">フォルダを読めません</string>
<string name="import_old_galleries_notification">旧ギャラリーインポート中…</string>
@@ -149,7 +133,6 @@
<string name="settings_download_when_cache_disable_warning">キャッシュを使用しないため、ダウンロードできません</string>
<string name="settings_user_id">ユーザーID</string>
<string name="settings_user_id_toast">ユーザーIDをクリップボードにコピーしました</string>
<string name="reader_error_retry">ダウンロードエラーが発生しました。リトライしますか?</string>
<string name="reader_fab_retry">リトライ</string>
<string name="reader_fab_auto">自動スクロール</string>
</resources>

View File

@@ -4,7 +4,6 @@
<string name="galleryblock_series">시리즈: %1$s</string>
<string name="galleryblock_type">종류: %1$s</string>
<string name="search_hint">갤러리 검색</string>
<string name="search_hint_with_page">갤러리 검색</string>
<string name="settings_default_query">기본 검색어</string>
<string name="settings_clear_cache">캐시 정리하기</string>
<string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?</string>
@@ -17,14 +16,12 @@
<string name="update_title">업데이트가 있습니다!</string>
<string name="warning">경고</string>
<string name="main_no_result">결과 없음</string>
<string name="main_search">검색</string>
<string name="settings_miscellaneous_title">기타</string>
<string name="settings_clear_history">기록 삭제</string>
<string name="settings_clear_history_alert_message">기록을 삭제하시겠습니까?</string>
<string name="settings_clear_history_summary">기록 %1$d개 저장됨</string>
<string name="main_drawer_history">기록</string>
<string name="main_drawer_home"></string>
<string name="update_download_started">다운로드 중</string>
<string name="update_release_note"># 릴리즈 노트(v%1$s)\n%2$s</string>
<string name="settings_security_mode_summary">최근 앱 목록 창에서 앱 화면을 보이지 않게 합니다</string>
<string name="settings_security_mode_title">보안 모드 활성화</string>
@@ -45,9 +42,7 @@
<string name="reader_fab_download">백그라운드 다운로드</string>
<string name="reader_notification_text">다운로드 중…</string>
<string name="reader_notification_complete">다운로드 완료</string>
<string name="reader_notification_error">다운로드 오류</string>
<string name="reader_fab_download_cancel">백그라운드 다운로드 취소</string>
<string name="main_dialog_delete">갤러리 삭제</string>
<string name="main_drawer_downloads">다운로드</string>
<string name="main_jump_title">페이지 이동</string>
<string name="main_jump_message">현재 페이지: %1$d\n페이지 수: %2$d</string>
@@ -55,10 +50,6 @@
<string name="main_move">%1$d 페이지로 이동</string>
<string name="https_block_alert_title">접속 불가 현상 안내</string>
<string name="https_block_alert">최근 https 차단으로 접속이 안 되는 경우가 발생하고 있습니다 이 경우 플레이스토어에서 Intra앱을 이용하시면 정상이용이 가능합니다.</string>
<string name="main_dialog_export">갤러리 내보내기</string>
<string name="main_export_complete">내보내기 완료</string>
<string name="main_export_open_folder">폴더 열기</string>
<string name="main_export_error">내보내기 오류가 발생했습니다</string>
<string name="settings_clear_downloads">다운로드 삭제</string>
<string name="settings_clear_downloads_alert_message">다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?</string>
<string name="main_drawer_favorite">즐겨찾기</string>
@@ -82,11 +73,8 @@
<string name="main_menu_sort">정렬</string>
<string name="main_menu_sort_popular">인기순</string>
<string name="main_menu_sort_newest">시간순</string>
<string name="update_failed">"업데이트 에러</string>
<string name="update_failed_message">업데이트 중 에러가 발생했습니다</string>
<string name="ignore_update">무시</string>
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
<string name="update_no_permission">권한이 부여되어 있지 않아 자동 업데이트를 진행할 수 없습니다. 홈페이지에서 직접 다운로드 받으시기 바랍니다.</string>
<string name="settings_dark_mode_title">다크 모드</string>
<string name="settings_dark_mode_summary">딥 다크한 모오드</string>
<string name="gallery_details">갤러리 정보</string>
@@ -99,13 +87,11 @@
<string name="gallery_related">관련 갤러리</string>
<string name="gallery_thumbnails">미리보기</string>
<string name="settings_nomedia_title">이미지 숨기기</string>
<string name="reader_help">도움말</string>
<string name="main_delete">삭제</string>
<string name="main_download">다운로드</string>
<string name="settings_backup_title">즐겨찾기 백업</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>
<string name="settings_dl_location">다운로드 위치</string>
@@ -134,8 +120,6 @@
<string name="main_fab_cancel">다운로드 모두 취소</string>
<string name="channel_update">업데이트</string>
<string name="channel_update_description">업데이트 진행상황 표시</string>
<string name="channel_import">가져오기</string>
<string name="channel_import_description">가져오기 상태 표시</string>
<string name="settings_import_old_galleries">이전 버전 갤러리 가져오기</string>
<string name="import_old_galleries_folder_not_readable">폴더를 읽을 수 없습니다</string>
<string name="import_old_galleries_notification">이전 버전 갤러리 가져오는 중…</string>
@@ -149,7 +133,6 @@
<string name="settings_download_when_cache_disable_warning">캐시를 활성화 해야 다운로드를 진행할 수 있습니다</string>
<string name="settings_user_id">유저 ID</string>
<string name="settings_user_id_toast">유저 ID를 클립보드에 복사했습니다</string>
<string name="reader_error_retry">다운로드 에러가 발생했습니. 재시도 하시겠습니까?</string>
<string name="reader_fab_retry">재시도</string>
<string name="reader_fab_auto">자동 스크롤</string>
</resources>

View File

@@ -6,16 +6,6 @@
<item>25</item>
<item>50</item>
</string-array>
<!-- Reply Preference -->
<string-array name="reply_entries">
<item>Reply</item>
<item>Reply to all</item>
</string-array>
<string-array name="reply_values">
<item>reply</item>
<item>reply_all</item>
</string-array>
<string-array name="languages">
<item>indonesian|Bahasa Indonesia</item>

View File

@@ -3,7 +3,6 @@
<color name="colorPrimary">#4fc3f7</color>
<color name="colorPrimaryDark">#0093c4</color>
<color name="colorAccent">#D81B60</color>
<color name="appbar">#FFFFFF</color>
<color name="material_pink_600">#d81b60</color>
<color name="material_blue_700">#1976d2</color>

View File

@@ -1,16 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="appbar_padding">64dp</dimen>
<dimen name="progress_view_start">32dp</dimen>
<dimen name="progress_view_offset">96dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">176dp</dimen>
<dimen name="thumbnail_margin">8dp</dimen>
<dimen name="galleryblock_thumbnail_thin">100dp</dimen>
<dimen name="galleryblock_thumbnail_normal">150dp</dimen>
</resources>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@@ -1,4 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
<item name="request_settings" type="id" />
<item name="request_lock" type="id" />
<item name="request_restore" type="id" />
<item name="request_import_old_galleries" type="id" />
<item name="request_import_old_galleries_old" type="id" />
<item name="request_download_folder" type="id" />
<item name="request_download_folder_old" type="id" />
<item name="request_write_permission_and_saf" type="id" />
<item name="notification_id_update" type="id" />
</resources>

View File

@@ -2,15 +2,12 @@
<string name="app_name" translatable="false" tools:override="true">Pupil</string>
<string name="release_url" translatable="false">https://api.github.com/repos/tom5079/Pupil/releases</string>
<string name="release_name" translatable="false">Pupil-v(\\d+\\.)+\\d+\\.apk</string>
<string name="home_page" translatable="false">http://bit.ly/2EZDClw</string>
<string name="update" translatable="false">http://bit.ly/2ZlOjXJ</string>
<string name="help" translatable="false">http://bit.ly/2Z7lNZE</string>
<string name="github" translatable="false">https://github.com/tom5079/Pupil/</string>
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
<string name="discord" translatable="false">https://discord.gg/Stj4b5v</string>
<string name="error_help" translatable="false">http://bit.ly/2KYYhto</string>
<string name="main_settings" translatable="false">Settings</string>
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
@@ -18,8 +15,6 @@
<string name="reader_imageview_description" translatable="false">Content ImageView</string>
<string name="page_indicator_placeholder" translatable="false">-/-</string>
<string name="plus_to_close" translatable="false">Fab</string>
<!-- Translate needed down here -->
<string name="warning">Warning</string>
@@ -27,9 +22,6 @@
<string name="https_block_alert_title">(Korean only)</string>
<string name="https_block_alert">(Korean only)</string>
<string name="update_failed">Update failed</string>
<string name="update_failed_message">Please install manually by visiting github release page :{ (or try again!)</string>
<string name="update_no_permission">Cannot auto update because permission is denied. Please download manually from the webpage.</string>
<string name="ignore_update">Ignore</string>
<string name="channel_download">Download</string>
@@ -38,14 +30,10 @@
<string name="channel_update">Update</string>
<string name="channel_update_description">Shows update progress</string>
<string name="channel_import">Import</string>
<string name="channel_import_description">Shows progress of import</string>
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
<string name="main_search">Search</string>
<string name="main_no_result">No result</string>
<string name="main_drawer_home">Home</string>
@@ -74,25 +62,16 @@
<string name="main_move">Move to page %1$d</string>
<string name="main_dialog_delete">Delete this gallery</string>
<string name="main_dialog_export">Export this gallery</string>
<string name="main_export_complete">Export completed</string>
<string name="main_export_open_folder">Open Folder</string>
<string name="main_export_error">Error occurred during export</string>
<string name="main_download">DOWNLOAD</string>
<string name="main_delete">DELETE</string>
<string name="update_title">Update available</string>
<string name="update_download_started">Download started</string>
<string name="update_download_completed">Download Completed</string>
<string name="update_download_completed_description">Click here to update</string>
<string name="update_notification_description">Downloading update&#8230;</string>
<string name="update_release_note"># Release Note(v%1$s)\n%2$s</string>
<string name="search_hint">Search galleries</string>
<string name="search_hint_with_page">Search galleries</string>
<string name="gallery_details">Details</string>
<string name="gallery_thumbnails">Thumbnails</string>
@@ -120,11 +99,6 @@
<string name="reader_fab_download_cancel">Cancel background download</string>
<string name="reader_notification_text">Downloading&#8230;</string>
<string name="reader_notification_complete">Download complete</string>
<string name="reader_notification_error">Download error</string>
<string name="reader_error_retry">Download Error. Retry?</string>
<string name="reader_help">Help</string>
<!-- SETTINGS -->
@@ -180,7 +154,6 @@
<string name="settings_nomedia_title">Hide 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>

View File

@@ -1,7 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.72'
repositories {
google()
jcenter()

View File

@@ -17,4 +17,6 @@ org.gradle.daemon=true
org.gradle.configureondemand=true
kotlin.code.style=official
android.enableJetifier=true
android.useAndroidX=true
android.useAndroidX=true
kotlin_version=1.4.0

View File

@@ -1 +1 @@
include ':app', ':libpupil'
include ':app'