Fast scroll Added
Not able to download after finished downloading to cache Fixed Trying to move files from different threads causing exceptions Fixed Import old galleries Added
This commit is contained in:
@@ -46,7 +46,7 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildToolsVersion = '29.0.2'
|
||||
buildToolsVersion = '29.0.3'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -66,7 +66,7 @@ dependencies {
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||
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.crashlytics.sdk.android:crashlytics:2.10.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 org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import xyz.quaver.hitomi.getGalleryIDsFromNozomi
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.createImgList
|
||||
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
|
||||
fun test_doSearch() {
|
||||
val reader = getReader( 1426382)
|
||||
|
||||
@@ -30,10 +30,17 @@ 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
|
||||
|
||||
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?) {
|
||||
context ?: return
|
||||
|
||||
@@ -87,6 +94,9 @@ class BroadcastReciever : BroadcastReceiver() {
|
||||
|
||||
notificationManager.notify(NOTIFICATION_ID_UPDATE, notification)
|
||||
}
|
||||
ACTION_CANCEL_IMPORT -> {
|
||||
cancelImport = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,13 @@ class Pupil : MultiDexApplication() {
|
||||
enableVibration(true)
|
||||
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)
|
||||
|
||||
@@ -414,18 +414,14 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
onDownloadClickedHandler = { position ->
|
||||
val galleryID = galleries[position].id
|
||||
val worker = DownloadWorker.getInstance(context)
|
||||
|
||||
if (!completeFlag.get(galleryID, false)) {
|
||||
val worker = DownloadWorker.getInstance(context)
|
||||
if (Cache(context).isDownloading(galleryID)) //download in progress
|
||||
worker.cancel(galleryID)
|
||||
else {
|
||||
Cache(context).setDownloading(galleryID, true)
|
||||
|
||||
if (Cache(context).isDownloading(galleryID)) //download in progress
|
||||
worker.cancel(galleryID)
|
||||
else {
|
||||
Cache(context).setDownloading(galleryID, true)
|
||||
|
||||
if (!worker.queue.contains(galleryID))
|
||||
worker.queue.add(galleryID)
|
||||
}
|
||||
worker.queue.add(galleryID)
|
||||
}
|
||||
|
||||
closeAllItems()
|
||||
@@ -477,6 +473,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
GalleryDialog(
|
||||
this@MainActivity,
|
||||
Glide.with(this@MainActivity),
|
||||
galleryID
|
||||
).apply {
|
||||
onChipClickedHandler.add {
|
||||
@@ -731,6 +728,16 @@ class MainActivity : AppCompatActivity() {
|
||||
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 {
|
||||
when(it.itemId) {
|
||||
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS)
|
||||
|
||||
@@ -156,6 +156,43 @@ class SettingsActivity : AppCompatActivity() {
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package xyz.quaver.pupil.ui.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -29,7 +30,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.snackbar.Snackbar
|
||||
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.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 {
|
||||
it.split("|").let { split ->
|
||||
@@ -61,8 +62,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
private val glide = Glide.with(context)
|
||||
|
||||
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -90,7 +89,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
try {
|
||||
val gallery = getGallery(galleryID)
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
gallery_cover.post {
|
||||
gallery_progressbar.visibility = View.GONE
|
||||
gallery_title.text = gallery.title
|
||||
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)
|
||||
.apply {
|
||||
if (BuildConfig.CENSOR)
|
||||
@@ -226,7 +225,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val galleries = ArrayList<GalleryBlock>()
|
||||
|
||||
val adapter = GalleryBlockAdapter(Glide.with(ownerActivity ?: return), galleries).apply {
|
||||
val adapter = GalleryBlockAdapter(glide, galleries).apply {
|
||||
onChipClickedHandler.add { tag ->
|
||||
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
||||
handler.invoke(tag)
|
||||
@@ -264,6 +263,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
.setOnItemLongClickListener { _, position, _ ->
|
||||
GalleryDialog(
|
||||
context,
|
||||
glide,
|
||||
galleries[position].id
|
||||
).apply {
|
||||
onChipClickedHandler.add { tag ->
|
||||
|
||||
@@ -18,17 +18,23 @@
|
||||
|
||||
package xyz.quaver.pupil.ui.fragment
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
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.R
|
||||
import xyz.quaver.pupil.ui.LockActivity
|
||||
@@ -168,6 +174,31 @@ class SettingsFragment :
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -297,6 +328,9 @@ class SettingsFragment :
|
||||
"restore" -> {
|
||||
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.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
|
||||
|
||||
@@ -38,11 +38,16 @@ import xyz.quaver.pupil.util.json
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.locks.Lock
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
class Cache(context: Context) : ContextWrapper(context) {
|
||||
|
||||
companion object {
|
||||
private val moving = mutableListOf<Int>()
|
||||
}
|
||||
|
||||
private val locks = SparseArray<Lock>()
|
||||
private fun lock(galleryID: Int) {
|
||||
synchronized(locks) {
|
||||
@@ -245,27 +250,32 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun moveToDownload(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
|
||||
val cache = getCachedGallery(galleryID).also {
|
||||
if (!it.exists())
|
||||
fun moveToDownload(galleryID: Int) {
|
||||
if (moving.contains(galleryID))
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@@ -158,10 +158,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
.body(ProgressResponseBody(request.tag(), response.body(), progressListener))
|
||||
.build()
|
||||
}
|
||||
|
||||
val client =
|
||||
OkHttpClient.Builder()
|
||||
.addInterceptor(interceptor)
|
||||
.connectTimeout(0, TimeUnit.SECONDS)
|
||||
.addInterceptor(interceptor)
|
||||
.readTimeout(0, TimeUnit.SECONDS)
|
||||
.dispatcher(Dispatcher(Executors.newFixedThreadPool(4)))
|
||||
.proxy(proxy)
|
||||
@@ -178,7 +179,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
worker[galleryID]?.cancel()
|
||||
}
|
||||
|
||||
client.dispatcher().cancelAll()
|
||||
client.dispatcher().queuedCalls().filter {
|
||||
it.request().tag() is Pair<*, *>
|
||||
}.forEach {
|
||||
it.cancel()
|
||||
}
|
||||
|
||||
progress.clear()
|
||||
exception.clear()
|
||||
@@ -411,7 +416,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
val galleryID = queue.peek() ?: continue
|
||||
|
||||
if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading!
|
||||
continue
|
||||
cancel(galleryID)
|
||||
|
||||
if (notification[galleryID] == null)
|
||||
initNotification(galleryID)
|
||||
|
||||
@@ -18,24 +18,40 @@
|
||||
|
||||
package xyz.quaver.pupil.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DownloadManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Base64
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.content
|
||||
import okhttp3.*
|
||||
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.R
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.Metadata
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
fun getReleases(url: String) : JsonArray {
|
||||
return try {
|
||||
@@ -172,4 +188,149 @@ fun checkUpdate(context: Context, force: Boolean = false) {
|
||||
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:paddingTop="64dp"
|
||||
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"/>
|
||||
|
||||
<com.github.clans.fab.FloatingActionMenu
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
android:id="@+id/reader_recyclerview"
|
||||
android:layout_width="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"/>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -134,4 +134,10 @@
|
||||
<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>
|
||||
<string name="import_old_galleries_notification_done">インポート完了</string>
|
||||
</resources>
|
||||
@@ -134,4 +134,10 @@
|
||||
<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>
|
||||
<string name="import_old_galleries_notification_done">가져오기 완료</string>
|
||||
</resources>
|
||||
@@ -38,6 +38,9 @@
|
||||
<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>
|
||||
@@ -173,6 +176,7 @@
|
||||
<string name="settings_restore_title">Restore favorites</string>
|
||||
<string name="settings_restore_failed">Restore failed</string>
|
||||
<string name="settings_restore_successful">%1$d entries restored</string>
|
||||
<string name="settings_import_old_galleries">Import old galleries</string>
|
||||
|
||||
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
||||
|
||||
@@ -205,4 +209,10 @@
|
||||
<string name="proxy_dialog_error">Wrong value</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>
|
||||
|
||||
@@ -99,6 +99,10 @@
|
||||
app:key="restore"
|
||||
app:title="@string/settings_restore_title"/>
|
||||
|
||||
<Preference
|
||||
app:key="old_import_galleries"
|
||||
app:title="@string/settings_import_old_galleries"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
||||
@@ -34,8 +34,6 @@ class ExampleUnitTest {
|
||||
@Test
|
||||
fun test() {
|
||||
val arr = SparseArray<Float>()
|
||||
|
||||
print(arr.indexOfKey(34))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user