Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3198c6cbfd | ||
|
|
b3feee6d9d | ||
|
|
f0f53e6bce |
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 50
|
versionCode 51
|
||||||
versionName "4.14"
|
versionName "4.15"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -46,7 +46,7 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
buildToolsVersion = '29.0.2'
|
buildToolsVersion = '29.0.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -66,7 +66,7 @@ dependencies {
|
|||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
||||||
implementation 'com.google.firebase:firebase-core:17.2.2'
|
implementation 'com.google.firebase:firebase-core:17.2.3'
|
||||||
implementation 'com.google.firebase:firebase-perf:19.0.5'
|
implementation 'com.google.firebase:firebase-perf:19.0.5'
|
||||||
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'
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":50,"versionName":"4.14","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
|
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":51,"versionName":"4.15","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
|
||||||
@@ -28,6 +28,7 @@ import androidx.test.rule.ActivityTestRule
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import xyz.quaver.hitomi.getGalleryIDsFromNozomi
|
||||||
import xyz.quaver.hiyobi.cookie
|
import xyz.quaver.hiyobi.cookie
|
||||||
import xyz.quaver.hiyobi.createImgList
|
import xyz.quaver.hiyobi.createImgList
|
||||||
import xyz.quaver.hiyobi.getReader
|
import xyz.quaver.hiyobi.getReader
|
||||||
@@ -62,6 +63,13 @@ class ExampleInstrumentedTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_nozomi() {
|
||||||
|
val nozomi = getGalleryIDsFromNozomi(null, "index", "all")
|
||||||
|
|
||||||
|
Log.i("PUPILD", nozomi.size.toString())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_doSearch() {
|
fun test_doSearch() {
|
||||||
val reader = getReader( 1426382)
|
val reader = getReader( 1426382)
|
||||||
|
|||||||
@@ -30,10 +30,17 @@ import androidx.core.app.NotificationManagerCompat
|
|||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import xyz.quaver.pupil.util.NOTIFICATION_ID_UPDATE
|
import xyz.quaver.pupil.util.NOTIFICATION_ID_UPDATE
|
||||||
|
import xyz.quaver.pupil.util.cancelImport
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class BroadcastReciever : BroadcastReceiver() {
|
class BroadcastReciever : BroadcastReceiver() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTION_CANCEL_IMPORT = "ACTION_CANCEL_IMPORT"
|
||||||
|
|
||||||
|
const val EXTRA_IMPORT_NOTIFICATION_ID = "EXTRA_IMPORT_NOTIFICATION_ID"
|
||||||
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
context ?: return
|
context ?: return
|
||||||
|
|
||||||
@@ -87,6 +94,9 @@ class BroadcastReciever : BroadcastReceiver() {
|
|||||||
|
|
||||||
notificationManager.notify(NOTIFICATION_ID_UPDATE, notification)
|
notificationManager.notify(NOTIFICATION_ID_UPDATE, notification)
|
||||||
}
|
}
|
||||||
|
ACTION_CANCEL_IMPORT -> {
|
||||||
|
cancelImport = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ class Pupil : MultiDexApplication() {
|
|||||||
enableVibration(true)
|
enableVibration(true)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
})
|
})
|
||||||
|
|
||||||
|
manager.createNotificationChannel(NotificationChannel("import", getString(R.string.channel_update), NotificationManager.IMPORTANCE_HIGH).apply {
|
||||||
|
description = getString(R.string.channel_update_description)
|
||||||
|
enableLights(false)
|
||||||
|
enableVibration(false)
|
||||||
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
|
|||||||
@@ -414,18 +414,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
onDownloadClickedHandler = { position ->
|
onDownloadClickedHandler = { position ->
|
||||||
val galleryID = galleries[position].id
|
val galleryID = galleries[position].id
|
||||||
|
val worker = DownloadWorker.getInstance(context)
|
||||||
|
|
||||||
if (!completeFlag.get(galleryID, false)) {
|
if (Cache(context).isDownloading(galleryID)) //download in progress
|
||||||
val worker = DownloadWorker.getInstance(context)
|
worker.cancel(galleryID)
|
||||||
|
else {
|
||||||
|
Cache(context).setDownloading(galleryID, true)
|
||||||
|
|
||||||
if (Cache(context).isDownloading(galleryID)) //download in progress
|
worker.queue.add(galleryID)
|
||||||
worker.cancel(galleryID)
|
|
||||||
else {
|
|
||||||
Cache(context).setDownloading(galleryID, true)
|
|
||||||
|
|
||||||
if (!worker.queue.contains(galleryID))
|
|
||||||
worker.queue.add(galleryID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeAllItems()
|
closeAllItems()
|
||||||
@@ -477,6 +473,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
GalleryDialog(
|
GalleryDialog(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
|
Glide.with(this@MainActivity),
|
||||||
galleryID
|
galleryID
|
||||||
).apply {
|
).apply {
|
||||||
onChipClickedHandler.add {
|
onChipClickedHandler.add {
|
||||||
@@ -731,6 +728,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
favoritesFile.writeText(json.stringify(serializer, Tags(listOf())))
|
favoritesFile.writeText(json.stringify(serializer, Tags(listOf())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
|
||||||
|
override fun onMenuOpened() {
|
||||||
|
(this@MainActivity.main_recyclerview.adapter as GalleryBlockAdapter).closeAllItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuClosed() {
|
||||||
|
//Do Nothing
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when(it.itemId) {
|
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), REQUEST_SETTINGS)
|
||||||
|
|||||||
@@ -158,10 +158,10 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
when(item?.itemId) {
|
when(item?.itemId) {
|
||||||
R.id.reader_menu_page_indicator -> {
|
R.id.reader_menu_page_indicator -> {
|
||||||
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
|
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false)
|
||||||
with(view.dialog_number_picker) {
|
with(view.dialog_number_picker) {
|
||||||
minValue=1
|
minValue=1
|
||||||
maxValue=reader_recyclerview?.adapter?.itemCount ?: 0
|
maxValue=Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.files?.size ?: 0
|
||||||
value=currentPage
|
value=currentPage
|
||||||
}
|
}
|
||||||
val dialog = AlertDialog.Builder(this).apply {
|
val dialog = AlertDialog.Builder(this).apply {
|
||||||
|
|||||||
@@ -156,6 +156,43 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
REQUEST_IMPORT_OLD_GALLERIES -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
data?.data?.also { uri ->
|
||||||
|
val takeFlags: Int =
|
||||||
|
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)
|
||||||
|
contentResolver.takePersistableUriPermission(uri, takeFlags)
|
||||||
|
|
||||||
|
val file = uri.toFile(this)
|
||||||
|
|
||||||
|
if (file?.canRead() != true)
|
||||||
|
Snackbar.make(
|
||||||
|
settings,
|
||||||
|
resources.getText(R.string.import_old_galleries_folder_not_readable),
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
else
|
||||||
|
importOldGalleries(this, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REQUEST_IMPORT_OLD_GALLERIES_OLD -> {
|
||||||
|
if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
|
||||||
|
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
|
||||||
|
|
||||||
|
if (!File(directory).canRead())
|
||||||
|
Snackbar.make(
|
||||||
|
settings,
|
||||||
|
resources.getText(R.string.import_old_galleries_folder_not_readable),
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
else {
|
||||||
|
importOldGalleries(this, File(directory))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
package xyz.quaver.pupil.ui.dialog
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -29,7 +30,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.RequestManager
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.dialog_gallery.*
|
import kotlinx.android.synthetic.main.dialog_gallery.*
|
||||||
@@ -53,7 +54,7 @@ import xyz.quaver.pupil.util.ItemClickSupport
|
|||||||
import xyz.quaver.pupil.util.download.Cache
|
import xyz.quaver.pupil.util.download.Cache
|
||||||
import xyz.quaver.pupil.util.wordCapitalize
|
import xyz.quaver.pupil.util.wordCapitalize
|
||||||
|
|
||||||
class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(context) {
|
class GalleryDialog(context: Context, private val glide: RequestManager, private val galleryID: Int) : Dialog(context) {
|
||||||
|
|
||||||
private val languages = context.resources.getStringArray(R.array.languages).map {
|
private val languages = context.resources.getStringArray(R.array.languages).map {
|
||||||
it.split("|").let { split ->
|
it.split("|").let { split ->
|
||||||
@@ -61,8 +62,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
}
|
}
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
private val glide = Glide.with(context)
|
|
||||||
|
|
||||||
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
|
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -90,7 +89,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
try {
|
try {
|
||||||
val gallery = getGallery(galleryID)
|
val gallery = getGallery(galleryID)
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
gallery_cover.post {
|
||||||
gallery_progressbar.visibility = View.GONE
|
gallery_progressbar.visibility = View.GONE
|
||||||
gallery_title.text = gallery.title
|
gallery_title.text = gallery.title
|
||||||
gallery_artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() }
|
gallery_artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() }
|
||||||
@@ -112,7 +111,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Glide.with(context)
|
glide
|
||||||
.load(gallery.cover)
|
.load(gallery.cover)
|
||||||
.apply {
|
.apply {
|
||||||
if (BuildConfig.CENSOR)
|
if (BuildConfig.CENSOR)
|
||||||
@@ -226,7 +225,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
val galleries = ArrayList<GalleryBlock>()
|
val galleries = ArrayList<GalleryBlock>()
|
||||||
|
|
||||||
val adapter = GalleryBlockAdapter(Glide.with(ownerActivity ?: return), galleries).apply {
|
val adapter = GalleryBlockAdapter(glide, galleries).apply {
|
||||||
onChipClickedHandler.add { tag ->
|
onChipClickedHandler.add { tag ->
|
||||||
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
||||||
handler.invoke(tag)
|
handler.invoke(tag)
|
||||||
@@ -264,6 +263,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
.setOnItemLongClickListener { _, position, _ ->
|
.setOnItemLongClickListener { _, position, _ ->
|
||||||
GalleryDialog(
|
GalleryDialog(
|
||||||
context,
|
context,
|
||||||
|
glide,
|
||||||
galleries[position].id
|
galleries[position].id
|
||||||
).apply {
|
).apply {
|
||||||
onChipClickedHandler.add { tag ->
|
onChipClickedHandler.add { tag ->
|
||||||
|
|||||||
@@ -18,17 +18,23 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
package xyz.quaver.pupil.ui.fragment
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import net.rdrei.android.dirchooser.DirectoryChooserActivity
|
||||||
|
import net.rdrei.android.dirchooser.DirectoryChooserConfig
|
||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.ui.LockActivity
|
import xyz.quaver.pupil.ui.LockActivity
|
||||||
@@ -168,6 +174,31 @@ class SettingsFragment :
|
|||||||
|
|
||||||
activity?.startActivityForResult(intent, REQUEST_RESTORE)
|
activity?.startActivityForResult(intent, REQUEST_RESTORE)
|
||||||
}
|
}
|
||||||
|
"old_import_galleries" -> {
|
||||||
|
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)
|
||||||
|
else {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
||||||
|
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity?.startActivityForResult(intent, REQUEST_IMPORT_OLD_GALLERIES)
|
||||||
|
}
|
||||||
|
} else { // Can't use SAF on old Androids!
|
||||||
|
val config = DirectoryChooserConfig.builder()
|
||||||
|
.newDirectoryName("Pupil")
|
||||||
|
.allowNewDirectoryNameModification(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val intent = Intent(context, DirectoryChooserActivity::class.java).apply {
|
||||||
|
putExtra(DirectoryChooserActivity.EXTRA_CONFIG, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity?.startActivityForResult(intent, REQUEST_IMPORT_OLD_GALLERIES_OLD)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,6 +328,9 @@ class SettingsFragment :
|
|||||||
"restore" -> {
|
"restore" -> {
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
|
"old_import_galleries" -> {
|
||||||
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,16 @@ package xyz.quaver.pupil.util
|
|||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
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_LOCK = 38238
|
||||||
const val REQUEST_RESTORE = 16546
|
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 = 3874
|
||||||
const val REQUEST_DOWNLOAD_FOLDER_OLD = 3425
|
const val REQUEST_DOWNLOAD_FOLDER_OLD = 3425
|
||||||
const val REQUEST_WRITE_PERMISSION_AND_SAF = 13900
|
const val REQUEST_WRITE_PERMISSION_AND_SAF = 13900
|
||||||
|
|||||||
@@ -38,11 +38,16 @@ import xyz.quaver.pupil.util.json
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.locks.Lock
|
import java.util.concurrent.locks.Lock
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
class Cache(context: Context) : ContextWrapper(context) {
|
class Cache(context: Context) : ContextWrapper(context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val moving = mutableListOf<Int>()
|
||||||
|
}
|
||||||
|
|
||||||
private val locks = SparseArray<Lock>()
|
private val locks = SparseArray<Lock>()
|
||||||
private fun lock(galleryID: Int) {
|
private fun lock(galleryID: Int) {
|
||||||
synchronized(locks) {
|
synchronized(locks) {
|
||||||
@@ -245,27 +250,32 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveToDownload(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
|
fun moveToDownload(galleryID: Int) {
|
||||||
val cache = getCachedGallery(galleryID).also {
|
if (moving.contains(galleryID))
|
||||||
if (!it.exists())
|
return
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val cache = getCachedGallery(galleryID).also {
|
||||||
|
if (!it.exists())
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val download = File(getDownloadDirectory(this@Cache), galleryID.toString())
|
||||||
|
|
||||||
|
if (download.isParentOf(cache))
|
||||||
return@launch
|
return@launch
|
||||||
|
|
||||||
|
Log.i("PUPILD", "MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
|
||||||
|
|
||||||
|
cache.copyRecursively(download, true) { file, err ->
|
||||||
|
Log.i("PUPILD", "MOVING ERROR ${file.canonicalPath} ${err.message}")
|
||||||
|
OnErrorAction.SKIP
|
||||||
|
}
|
||||||
|
Log.i("PUPILD", "MOVED ${cache.canonicalPath}")
|
||||||
|
|
||||||
|
Log.i("PUPILD", "DELETING ${cache.canonicalPath}")
|
||||||
|
cache.deleteRecursively()
|
||||||
|
Log.i("PUPILD", "DELETED ${cache.canonicalPath}")
|
||||||
}
|
}
|
||||||
val download = File(getDownloadDirectory(this@Cache), galleryID.toString())
|
|
||||||
|
|
||||||
if (download.isParentOf(cache))
|
|
||||||
return@launch
|
|
||||||
|
|
||||||
Log.i("PUPILD", "MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
|
|
||||||
|
|
||||||
cache.copyRecursively(download, true) { file, err ->
|
|
||||||
Log.i("PUPILD", "MOVING ERROR ${file.canonicalPath} ${err.message}")
|
|
||||||
OnErrorAction.SKIP
|
|
||||||
}
|
|
||||||
Log.i("PUPILD", "MOVED ${cache.canonicalPath}")
|
|
||||||
|
|
||||||
Log.i("PUPILD", "DELETING ${cache.canonicalPath}")
|
|
||||||
cache.deleteRecursively()
|
|
||||||
Log.i("PUPILD", "DELETED ${cache.canonicalPath}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true
|
fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true
|
||||||
|
|||||||
@@ -158,10 +158,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
|
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
val client =
|
val client =
|
||||||
OkHttpClient.Builder()
|
OkHttpClient.Builder()
|
||||||
.addInterceptor(interceptor)
|
|
||||||
.connectTimeout(0, TimeUnit.SECONDS)
|
.connectTimeout(0, TimeUnit.SECONDS)
|
||||||
|
.addInterceptor(interceptor)
|
||||||
.readTimeout(0, TimeUnit.SECONDS)
|
.readTimeout(0, TimeUnit.SECONDS)
|
||||||
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
|
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
|
||||||
.proxy(proxy)
|
.proxy(proxy)
|
||||||
@@ -178,7 +179,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
worker[galleryID]?.cancel()
|
worker[galleryID]?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
client.dispatcher().cancelAll()
|
client.dispatcher().queuedCalls().filter {
|
||||||
|
it.request().tag() is Pair<*, *>
|
||||||
|
}.forEach {
|
||||||
|
it.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
progress.clear()
|
progress.clear()
|
||||||
exception.clear()
|
exception.clear()
|
||||||
@@ -411,7 +416,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
val galleryID = queue.peek() ?: continue
|
val galleryID = queue.peek() ?: continue
|
||||||
|
|
||||||
if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading!
|
if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading!
|
||||||
continue
|
cancel(galleryID)
|
||||||
|
|
||||||
if (notification[galleryID] == null)
|
if (notification[galleryID] == null)
|
||||||
initNotification(galleryID)
|
initNotification(galleryID)
|
||||||
|
|||||||
@@ -18,24 +18,40 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.util
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Base64
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.boolean
|
import kotlinx.serialization.json.boolean
|
||||||
import kotlinx.serialization.json.content
|
import kotlinx.serialization.json.content
|
||||||
|
import okhttp3.*
|
||||||
import ru.noties.markwon.Markwon
|
import ru.noties.markwon.Markwon
|
||||||
|
import xyz.quaver.hitomi.GalleryBlock
|
||||||
|
import xyz.quaver.hitomi.Reader
|
||||||
|
import xyz.quaver.hitomi.getGalleryBlock
|
||||||
|
import xyz.quaver.hitomi.getReader
|
||||||
|
import xyz.quaver.proxy
|
||||||
|
import xyz.quaver.pupil.BroadcastReciever
|
||||||
import xyz.quaver.pupil.BuildConfig
|
import xyz.quaver.pupil.BuildConfig
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
|
import xyz.quaver.pupil.util.download.Cache
|
||||||
|
import xyz.quaver.pupil.util.download.Metadata
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
fun getReleases(url: String) : JsonArray {
|
fun getReleases(url: String) : JsonArray {
|
||||||
return try {
|
return try {
|
||||||
@@ -172,4 +188,149 @@ fun checkUpdate(context: Context, force: Boolean = false) {
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cancelImport = false
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
fun importOldGalleries(context: Context, folder: File) = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val client = OkHttpClient.Builder()
|
||||||
|
.connectTimeout(0, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(0, TimeUnit.SECONDS)
|
||||||
|
.proxy(proxy)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val cancelIntent = Intent(context, BroadcastReciever::class.java).apply {
|
||||||
|
action = BroadcastReciever.ACTION_CANCEL_IMPORT
|
||||||
|
putExtra(BroadcastReciever.EXTRA_IMPORT_NOTIFICATION_ID, 0)
|
||||||
|
}
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(context, 0, cancelIntent, 0)
|
||||||
|
|
||||||
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
val notificationBuilder = NotificationCompat.Builder(context, "import").apply {
|
||||||
|
setContentTitle(context.getText(R.string.import_old_galleries_notification))
|
||||||
|
setProgress(0, 0, true)
|
||||||
|
setSmallIcon(R.drawable.ic_notification)
|
||||||
|
addAction(0, context.getText(android.R.string.cancel), pendingIntent)
|
||||||
|
setOngoing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager.notify(0, notificationBuilder.build())
|
||||||
|
|
||||||
|
if (!folder.isDirectory)
|
||||||
|
return@launch
|
||||||
|
|
||||||
|
val galleryRegex = Regex("""[0-9]+$""")
|
||||||
|
val imageRegex = Regex("""^[0-9]+\..+$""")
|
||||||
|
var size = 0
|
||||||
|
fun setProgress(progress: Int) {
|
||||||
|
notificationBuilder.apply {
|
||||||
|
setContentText(
|
||||||
|
context.getString(
|
||||||
|
R.string.import_old_galleries_notification_text,
|
||||||
|
progress,
|
||||||
|
size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setProgress(size, progress, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager.notify(0, notificationBuilder.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
folder.listFiles { _, name ->
|
||||||
|
galleryRegex.matches(name)
|
||||||
|
}?.also {
|
||||||
|
size = it.size
|
||||||
|
setProgress(0)
|
||||||
|
}?.forEachIndexed { index, gallery ->
|
||||||
|
if (cancelImport)
|
||||||
|
return@forEachIndexed
|
||||||
|
|
||||||
|
setProgress(index)
|
||||||
|
|
||||||
|
val galleryID = gallery.name.toIntOrNull() ?: return@forEachIndexed
|
||||||
|
|
||||||
|
File(getDownloadDirectory(context), galleryID.toString()).mkdirs()
|
||||||
|
|
||||||
|
val reader = async {
|
||||||
|
kotlin.runCatching {
|
||||||
|
json.parse(Reader.serializer(), File(gallery, "reader.json").readText())
|
||||||
|
}.getOrElse {
|
||||||
|
getReader(galleryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val galleryBlock = async {
|
||||||
|
kotlin.runCatching {
|
||||||
|
json.parse(GalleryBlock.serializer(), File(gallery, "galleryBlock.json").readText())
|
||||||
|
}.getOrElse {
|
||||||
|
getGalleryBlock(galleryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
val thumbnail = async thumbnail@{
|
||||||
|
val galleryBlock = galleryBlock.await()
|
||||||
|
|
||||||
|
Base64.encodeToString(try {
|
||||||
|
File(gallery, "thumbnail.jpg").readBytes()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val url = galleryBlock?.thumbnails?.firstOrNull()
|
||||||
|
|
||||||
|
if (url == null)
|
||||||
|
null
|
||||||
|
else {
|
||||||
|
val request = Request.Builder().url(url).build()
|
||||||
|
|
||||||
|
var done = false
|
||||||
|
var result: ByteArray? = null
|
||||||
|
client.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call?, e: IOException?) {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call?, response: Response?) {
|
||||||
|
result = response?.body()?.use {
|
||||||
|
it.bytes()
|
||||||
|
}
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!done)
|
||||||
|
yield()
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
} ?: return@thumbnail null, Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache(context).setCachedMetadata(galleryID,
|
||||||
|
Metadata(
|
||||||
|
thumbnail.await(),
|
||||||
|
galleryBlock.await(),
|
||||||
|
reader.await()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
File(gallery, "images").listFiles { _, name ->
|
||||||
|
imageRegex.matches(name)
|
||||||
|
}?.forEach {
|
||||||
|
if (cancelImport)
|
||||||
|
return@forEach
|
||||||
|
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
val index = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
|
Cache(context).putImage(galleryID, index, it.extension, it.inputStream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationBuilder.apply {
|
||||||
|
setContentText(context.getText(R.string.import_old_galleries_notification_done))
|
||||||
|
setProgress(0, 0, false)
|
||||||
|
setOngoing(false)
|
||||||
|
mActions.clear()
|
||||||
|
}
|
||||||
|
notificationManager.notify(0, notificationBuilder.build())
|
||||||
|
|
||||||
|
cancelImport = false
|
||||||
}
|
}
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 255 B |
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 311 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 432 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 605 B |
34
app/src/main/res/drawable/thumb.xml
Normal file
34
app/src/main/res/drawable/thumb.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="44dp"
|
||||||
|
android:topRightRadius="44dp"
|
||||||
|
android:bottomLeftRadius="44dp" />
|
||||||
|
|
||||||
|
<padding
|
||||||
|
android:paddingLeft="22dp"
|
||||||
|
android:paddingRight="22dp" />
|
||||||
|
|
||||||
|
<solid android:color="@color/colorPrimary" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
27
app/src/main/res/drawable/thumb_drawable.xml
Normal file
27
app/src/main/res/drawable/thumb_drawable.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:state_pressed="true"
|
||||||
|
android:drawable="@drawable/thumb"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/thumb"/>
|
||||||
|
</selector>
|
||||||
30
app/src/main/res/drawable/track.xml
Normal file
30
app/src/main/res/drawable/track.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
|
||||||
|
<padding
|
||||||
|
android:top="10dp"
|
||||||
|
android:left="10dp"
|
||||||
|
android:right="10dp"
|
||||||
|
android:bottom="10dp"/>
|
||||||
|
</shape>
|
||||||
27
app/src/main/res/drawable/track_drawable.xml
Normal file
27
app/src/main/res/drawable/track_drawable.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:state_pressed="true"
|
||||||
|
android:drawable="@drawable/track"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/track"/>
|
||||||
|
</selector>
|
||||||
@@ -71,7 +71,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingTop="64dp"
|
android:paddingTop="64dp"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:scrollbars="vertical"
|
app:fastScrollEnabled="true"
|
||||||
|
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
|
||||||
|
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
|
||||||
|
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable"
|
||||||
|
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionMenu
|
<com.github.clans.fab.FloatingActionMenu
|
||||||
|
|||||||
@@ -30,6 +30,11 @@
|
|||||||
android:id="@+id/reader_recyclerview"
|
android:id="@+id/reader_recyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:fastScrollEnabled="true"
|
||||||
|
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
|
||||||
|
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
|
||||||
|
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable"
|
||||||
|
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -134,4 +134,10 @@
|
|||||||
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
|
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
|
||||||
<string name="channel_update">アップデート</string>
|
<string name="channel_update">アップデート</string>
|
||||||
<string name="channel_update_description">アップデートの進行状態を表示</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>
|
||||||
|
<string name="import_old_galleries_notification_done">インポート完了</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -134,4 +134,10 @@
|
|||||||
<string name="main_fab_cancel">다운로드 모두 취소</string>
|
<string name="main_fab_cancel">다운로드 모두 취소</string>
|
||||||
<string name="channel_update">업데이트</string>
|
<string name="channel_update">업데이트</string>
|
||||||
<string name="channel_update_description">업데이트 진행상황 표시</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>
|
||||||
|
<string name="import_old_galleries_notification_done">가져오기 완료</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -38,6 +38,9 @@
|
|||||||
<string name="channel_update">Update</string>
|
<string name="channel_update">Update</string>
|
||||||
<string name="channel_update_description">Shows update progress</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="unable_to_connect">Unable to connect to hitomi.la</string>
|
||||||
|
|
||||||
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
|
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
|
||||||
@@ -173,6 +176,7 @@
|
|||||||
<string name="settings_restore_title">Restore favorites</string>
|
<string name="settings_restore_title">Restore favorites</string>
|
||||||
<string name="settings_restore_failed">Restore failed</string>
|
<string name="settings_restore_failed">Restore failed</string>
|
||||||
<string name="settings_restore_successful">%1$d entries restored</string>
|
<string name="settings_restore_successful">%1$d entries restored</string>
|
||||||
|
<string name="settings_import_old_galleries">Import old galleries</string>
|
||||||
|
|
||||||
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
||||||
|
|
||||||
@@ -205,4 +209,10 @@
|
|||||||
<string name="proxy_dialog_error">Wrong value</string>
|
<string name="proxy_dialog_error">Wrong value</string>
|
||||||
<string name="proxy_dialog_server">server</string>
|
<string name="proxy_dialog_server">server</string>
|
||||||
|
|
||||||
|
<!-- IMPORT OLD GALLERIES -->
|
||||||
|
<string name="import_old_galleries_folder_not_readable">This folder is not readable</string>
|
||||||
|
<string name="import_old_galleries_notification">Importing old galleries…</string>
|
||||||
|
<string name="import_old_galleries_notification_text" translatable="false">%1$d/%2$d</string>
|
||||||
|
<string name="import_old_galleries_notification_done">Importing completed</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -99,6 +99,10 @@
|
|||||||
app:key="restore"
|
app:key="restore"
|
||||||
app:title="@string/settings_restore_title"/>
|
app:title="@string/settings_restore_title"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="old_import_galleries"
|
||||||
|
app:title="@string/settings_import_old_galleries"/>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ class ExampleUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
fun test() {
|
fun test() {
|
||||||
val arr = SparseArray<Float>()
|
val arr = SparseArray<Float>()
|
||||||
|
|
||||||
print(arr.indexOfKey(34))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user