diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
index 663d6fc3..6d4686e3 100644
--- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt
@@ -20,8 +20,8 @@
package xyz.quaver.pupil
-import android.content.Intent
import android.util.Log
+import androidx.core.content.ContextCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
@@ -64,7 +64,9 @@ class ExampleInstrumentedTest {
val activityTestRule = ActivityTestRule(LockActivity::class.java)
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- activityTestRule.launchActivity(Intent())
+ ContextCompat.getExternalFilesDirs(appContext, null).forEachIndexed { index, file ->
+ Log.i("PUPILD", "$index: ${file?.absolutePath}")
+ }
}
@Test
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d4cf3be9..4db90b6f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,9 +4,6 @@
package="xyz.quaver.pupil">
-
diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
index 52b9a947..efc52b3b 100644
--- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt
+++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
@@ -62,19 +62,15 @@ class Pupil : MultiDexApplication() {
e.printStackTrace()
}
- if (!preference.getBoolean("channel_created", false)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val channel = NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply {
- description = getString(R.string.channel_download_description)
- enableLights(false)
- enableVibration(false)
- lockscreenVisibility = Notification.VISIBILITY_SECRET
- }
- manager.createNotificationChannel(channel)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val channel = NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply {
+ description = getString(R.string.channel_download_description)
+ enableLights(false)
+ enableVibration(false)
+ lockscreenVisibility = Notification.VISIBILITY_SECRET
}
-
- preference.edit().putBoolean("channel_created", true).apply()
+ manager.createNotificationChannel(channel)
}
AppCompatDelegate.setDefaultNightMode(when (preference.getBoolean("dark_mode", false)) {
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
index f268d9a0..a536dd36 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -18,17 +18,11 @@
package xyz.quaver.pupil.ui
-import android.Manifest
import android.app.Activity
-import android.app.DownloadManager
-import android.content.BroadcastReceiver
-import android.content.Context
import android.content.Intent
-import android.content.IntentFilter
import android.graphics.drawable.Animatable
import android.net.Uri
import android.os.Bundle
-import android.os.Environment
import android.text.*
import android.text.style.AlignmentSpan
import android.view.KeyEvent
@@ -42,7 +36,6 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
-import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.GravityCompat
@@ -60,11 +53,8 @@ import kotlinx.coroutines.*
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.content
import kotlinx.serialization.list
import kotlinx.serialization.stringify
-import ru.noties.markwon.Markwon
import xyz.quaver.hitomi.*
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
@@ -72,6 +62,7 @@ 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
import xyz.quaver.pupil.util.*
import java.io.File
import java.io.FileOutputStream
@@ -143,8 +134,6 @@ class MainActivity : AppCompatActivity() {
if (lockManager.isNotEmpty())
startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK)
- checkPermissions()
-
val preference = PreferenceManager.getDefaultSharedPreferences(this)
if (Locale.getDefault().language == "ko") {
@@ -167,7 +156,7 @@ class MainActivity : AppCompatActivity() {
setContentView(R.layout.activity_main)
- checkUpdate()
+ checkUpdate(this)
initView()
}
@@ -256,125 +245,6 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun checkUpdate() {
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(this)
- val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
-
- if (ignoreUpdateUntil > System.currentTimeMillis())
- return
-
- fun extractReleaseNote(update: JsonObject, locale: String) : String {
- val markdown = update["body"]!!.content
-
- val target = when(locale) {
- "ko" -> "한국어"
- "ja" -> "日本語"
- else -> "English"
- }
-
- val releaseNote = Regex("^# Release Note.+$")
- val language = Regex("^## $target$")
- val end = Regex("^#.+$")
-
- var releaseNoteFlag = false
- var languageFlag = false
-
- val result = StringBuilder()
-
- for(line in markdown.lines()) {
- if (releaseNote.matches(line)) {
- releaseNoteFlag = true
- continue
- }
-
- if (releaseNoteFlag) {
- if (language.matches(line)) {
- languageFlag = true
- continue
- }
- }
-
- if (languageFlag) {
- if (end.matches(line))
- break
-
- result.append(line+"\n")
- }
- }
-
- return getString(R.string.update_release_note, update["tag_name"]?.content, result.toString())
- }
-
- CoroutineScope(Dispatchers.Default).launch {
- val update =
- checkUpdate(getString(R.string.release_url)) ?: return@launch
-
- val (url, fileName) = getApkUrl(update) ?: return@launch
- fileName ?: return@launch
-
- val dialog = AlertDialog.Builder(this@MainActivity).apply {
- setTitle(R.string.update_title)
- val msg = extractReleaseNote(update, Locale.getDefault().language)
- setMessage(Markwon.create(context).toMarkdown(msg))
- setPositiveButton(android.R.string.yes) { _, _ ->
- if (!this@MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- AlertDialog.Builder(this@MainActivity).apply {
- setTitle(R.string.warning)
- setMessage(R.string.update_no_permission)
- setPositiveButton(android.R.string.ok) { _, _ -> }
- }.show()
-
- return@setPositiveButton
- }
-
- val request = DownloadManager.Request(Uri.parse(url)).apply {
- setDescription(getString(R.string.update_notification_description))
- setTitle(getString(R.string.app_name))
- setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
- }
-
- val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
- val id = manager.enqueue(request)
-
- registerReceiver(object: BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- try {
- val install = Intent(Intent.ACTION_VIEW).apply {
- flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
- setDataAndType(manager.getUriForDownloadedFile(id), manager.getMimeTypeForDownloadedFile(id))
- }
-
- startActivity(install)
- unregisterReceiver(this)
- } catch (e: Exception) {
- AlertDialog.Builder(this@MainActivity).apply {
- setTitle(R.string.update_failed)
- setMessage(R.string.update_failed_message)
- setPositiveButton(android.R.string.ok) { _, _ -> }
- }.show()
- }
- }
- }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
- }
- setNegativeButton(R.string.ignore_update) { _, _ ->
- preferences.edit()
- .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
- .apply()
- }
- }
-
- launch(Dispatchers.Main) {
- dialog.show()
- }
- }
- }
-
- private fun checkPermissions() {
- if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
- ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489)
- }
-
private fun initView() {
var prevP1 = 0
main_appbar_layout.addOnOffsetChangedListener(
@@ -613,7 +483,10 @@ class MainActivity : AppCompatActivity() {
val galleryID = galleries[position].first.id
- GalleryDialog(this@MainActivity, galleryID).apply {
+ GalleryDialog(
+ this@MainActivity,
+ galleryID
+ ).apply {
onChipClickedHandler.add {
runOnUiThread {
query = it.toQuery()
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
index 60f9a724..291c7753 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
@@ -44,8 +44,10 @@ import kotlinx.serialization.parseList
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tags
+import xyz.quaver.pupil.ui.dialog.DownloadLocationDialog
import xyz.quaver.pupil.util.Lock
import xyz.quaver.pupil.util.LockManager
+import xyz.quaver.pupil.util.byteToString
import xyz.quaver.pupil.util.getDownloadDirectory
import java.io.File
import java.nio.charset.Charset
@@ -85,14 +87,6 @@ class SettingsActivity : AppCompatActivity() {
class SettingsFragment : PreferenceFragmentCompat() {
- private val suffix = listOf(
- "B",
- "kB",
- "MB",
- "GB",
- "TB" //really?
- )
-
override fun onResume() {
super.onResume()
@@ -112,15 +106,9 @@ class SettingsActivity : AppCompatActivity() {
}
private fun getDirSize(dir: File) : String {
- var size = dir.walk().map { it.length() }.sum()
- var suffixIndex = 0
+ val size = dir.walk().map { it.length() }.sum()
- while (size >= 1024) {
- size /= 1024
- suffixIndex++
- }
-
- return getString(R.string.settings_clear_summary, size, suffix[suffixIndex])
+ return getString(R.string.settings_clear_summary, byteToString(size))
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -208,6 +196,25 @@ class SettingsActivity : AppCompatActivity() {
}
}
+ with(findPreference("dl_location")) {
+ this!!
+
+ summary = getDownloadDirectory(context).absolutePath
+
+ onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ DownloadLocationDialog(context).apply {
+ onDownloadLocationChangedListener = { value ->
+ PreferenceManager.getDefaultSharedPreferences(context).edit()
+ .putInt(key, value)
+ .apply()
+ summary = getDownloadDirectory(context).absolutePath
+ }
+ }.show()
+
+ true
+ }
+ }
+
with(findPreference("default_query")) {
this!!
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt
new file mode 100644
index 00000000..3fcae22a
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadLocationDialog.kt
@@ -0,0 +1,78 @@
+/*
+ * 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 .
+ */
+
+package xyz.quaver.pupil.ui.dialog
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.widget.LinearLayout
+import android.widget.RadioButton
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
+import kotlinx.android.synthetic.main.item_dl_location.view.*
+import xyz.quaver.pupil.R
+import xyz.quaver.pupil.util.byteToString
+
+@SuppressLint("InflateParams")
+class DownloadLocationDialog(context: Context) : AlertDialog(context) {
+
+ private val preference = PreferenceManager.getDefaultSharedPreferences(context)
+ private val buttons = mutableListOf()
+ var onDownloadLocationChangedListener : ((Int) -> (Unit))? = null
+
+ init {
+ val view = layoutInflater.inflate(R.layout.dialog_dl_location, null) as LinearLayout
+
+ ContextCompat.getExternalFilesDirs(context, null).forEachIndexed { index, dir ->
+
+ dir ?: return@forEachIndexed
+
+ view.addView(layoutInflater.inflate(R.layout.item_dl_location, view, false).apply {
+ location_type.text = context.getString(when (index) {
+ 0 -> R.string.settings_dl_location_internal
+ else -> R.string.settings_dl_location_removable
+ })
+ location_available.text = context.getString(
+ R.string.settings_dl_location_available,
+ byteToString(dir.freeSpace)
+ )
+ setOnClickListener {
+ buttons.forEach { button ->
+ button.isChecked = false
+ }
+ button.performClick()
+ onDownloadLocationChangedListener?.invoke(index)
+ }
+ buttons.add(button)
+ })
+ }
+
+ buttons[preference.getInt("dl_location", 0)].isChecked = true
+
+ setTitle(R.string.settings_dl_location)
+
+ setView(view)
+
+ setButton(Dialog.BUTTON_POSITIVE, context.getText(android.R.string.ok)) { _, _ ->
+ dismiss()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt
similarity index 97%
rename from app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt
rename to app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt
index aa02bbea..8896a4f8 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt
@@ -1,6 +1,6 @@
/*
* Pupil, Hitomi.la viewer for Android
- * Copyright (C) 2019 tom5079
+ * 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
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package xyz.quaver.pupil.ui
+package xyz.quaver.pupil.ui.dialog
import android.app.Dialog
import android.content.Context
@@ -46,6 +46,7 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.adapters.ThumbnailAdapter
import xyz.quaver.pupil.types.Tag
+import xyz.quaver.pupil.ui.ReaderActivity
import xyz.quaver.pupil.util.ItemClickSupport
import xyz.quaver.pupil.util.wordCapitalize
@@ -256,7 +257,10 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
(context.applicationContext as Pupil).histories.add(galleries[position].first.id)
}
.setOnItemLongClickListener { _, position, _ ->
- GalleryDialog(context, galleries[position].first.id).apply {
+ GalleryDialog(
+ context,
+ galleries[position].first.id
+ ).apply {
onChipClickedHandler.add { tag ->
this@GalleryDialog.onChipClickedHandler.forEach { it.invoke(tag) }
}
diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
index 07538401..2eb58b08 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt
@@ -163,8 +163,6 @@ class GalleryDownloader(
}
}
- private fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
-
fun start() {
downloadJob = CoroutineScope(Dispatchers.Default).launch {
val reader = reader!!.await() ?: return@launch
diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt
index 84342718..dd38730b 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/file.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt
@@ -19,9 +19,10 @@
package xyz.quaver.pupil.util
import android.content.Context
-import android.os.Build
-import android.os.Environment
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
import java.io.File
+import java.net.URL
fun getCachedGallery(context: Context, galleryID: Int): File {
return File(getDownloadDirectory(context), galleryID.toString()).let {
@@ -32,10 +33,32 @@ fun getCachedGallery(context: Context, galleryID: Int): File {
}
}
-@Suppress("DEPRECATION")
fun getDownloadDirectory(context: Context): File {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- context.getExternalFilesDir("Pupil")!!
- else
- File(Environment.getExternalStorageDirectory(), "Pupil")
+ val dlLocation = PreferenceManager.getDefaultSharedPreferences(context).getInt("dl_location", 0)
+
+ return ContextCompat.getExternalFilesDirs(context, null)[dlLocation]
+}
+
+fun URL.download(to: File, onDownloadProgress: ((Long, Long) -> Unit)? = null) {
+ to.outputStream().use { out ->
+
+ with(openConnection()) {
+ val fileSize = contentLength.toLong()
+
+ getInputStream().use {
+
+ var bytesCopied: Long = 0
+ val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
+ var bytes = it.read(buffer)
+ while (bytes >= 0) {
+ out.write(buffer, 0, bytes)
+ bytesCopied += bytes
+ onDownloadProgress?.invoke(bytesCopied, fileSize)
+ bytes = it.read(buffer)
+ }
+
+ }
+ }
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt
index 05deb0fc..87b38bb6 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt
@@ -24,6 +24,7 @@ import android.os.Build
import androidx.core.content.ContextCompat
import java.util.*
import kotlin.collections.ArrayList
+import kotlin.math.round
//Android Q+ uses scoped storage thus not requiring permission
fun Context.hasPermission(permission: String) =
@@ -37,4 +38,33 @@ fun String.wordCapitalize() : String {
result.add(word.capitalize(Locale.getDefault()))
return result.joinToString(" ")
+}
+
+
+//https://discuss.kotlinlang.org/t/how-do-you-round-a-number-to-n-decimal-places/8843(fvasco)
+fun Double.round(decimals: Int): Double {
+ var multiplier = 1.0
+ repeat(decimals) { multiplier *= 10 }
+ return round(this * multiplier) / multiplier
+}
+
+fun byteToString(byte: Long, precision : Int = 1) : String {
+
+ val suffix = listOf(
+ "B",
+ "kB",
+ "MB",
+ "GB",
+ "TB" //really?
+ )
+ var size = byte.toDouble()
+ var suffixIndex = 0
+
+ while (size >= 1024) {
+ size /= 1024
+ suffixIndex++
+ }
+
+ return "${size.round(precision)} ${suffix[suffixIndex]}"
+
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt
index c42c13d8..13266ae6 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/update.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt
@@ -18,15 +18,31 @@
package xyz.quaver.pupil.util
+import android.app.PendingIntent
import android.content.Context
+import android.content.Intent
+import android.webkit.MimeTypeMap
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.FileProvider
+import androidx.lifecycle.Lifecycle
+import androidx.preference.PreferenceManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.internal.EnumSerializer
import kotlinx.serialization.json.*
+import ru.noties.markwon.Markwon
import xyz.quaver.availableInHiyobi
import xyz.quaver.hitomi.Reader
import xyz.quaver.pupil.BuildConfig
+import xyz.quaver.pupil.R
import java.io.File
import java.net.URL
+import java.util.*
fun getReleases(url: String) : JsonArray {
return try {
@@ -58,14 +74,127 @@ fun checkUpdate(url: String) : JsonObject? {
}
}
-fun getApkUrl(releases: JsonObject) : Pair? {
+fun getApkUrl(releases: JsonObject) : String? {
return releases["assets"]?.jsonArray?.firstOrNull {
Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.content ?: "")
}.let {
- if (it == null)
- null
- else
- Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
+ it?.jsonObject?.get("browser_download_url")?.content
+ }
+}
+
+val UPDATE_NOTIFICATION_ID = 384823
+fun checkUpdate(context: AppCompatActivity) {
+
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
+
+ if (ignoreUpdateUntil > System.currentTimeMillis())
+ return
+
+ fun extractReleaseNote(update: JsonObject, locale: Locale) : String {
+ val markdown = update["body"]!!.content
+
+ val target = when(locale) {
+ Locale.KOREAN -> "한국어"
+ Locale.JAPANESE -> "日本語"
+ else -> "English"
+ }
+
+ val releaseNote = Regex("^# Release Note.+$")
+ val language = Regex("^## $target$")
+ val end = Regex("^#.+$")
+
+ var releaseNoteFlag = false
+ var languageFlag = false
+
+ val result = StringBuilder()
+
+ for(line in markdown.lines()) {
+ if (releaseNote.matches(line)) {
+ releaseNoteFlag = true
+ continue
+ }
+
+ if (releaseNoteFlag) {
+ if (language.matches(line)) {
+ languageFlag = true
+ continue
+ }
+ }
+
+ if (languageFlag) {
+ if (end.matches(line))
+ break
+
+ result.append(line+"\n")
+ }
+ }
+
+ return context.getString(R.string.update_release_note, update["tag_name"]?.content, result.toString())
+ }
+
+ CoroutineScope(Dispatchers.Default).launch {
+ val update =
+ checkUpdate(context.getString(R.string.release_url)) ?: return@launch
+
+ val url = getApkUrl(update) ?: return@launch
+
+ val dialog = AlertDialog.Builder(context).apply {
+ setTitle(R.string.update_title)
+ val msg = extractReleaseNote(update, Locale.getDefault())
+ setMessage(Markwon.create(context).toMarkdown(msg))
+ setPositiveButton(android.R.string.yes) { _, _ ->
+
+ val notificationManager = NotificationManagerCompat.from(context)
+ val builder = NotificationCompat.Builder(context, "download").apply {
+ setContentTitle(context.getString(R.string.update_notification_description))
+ setSmallIcon(android.R.drawable.stat_sys_download)
+ priority = NotificationCompat.PRIORITY_LOW
+ }
+
+ CoroutineScope(Dispatchers.IO).launch {
+ val target = File(getDownloadDirectory(context), "Pupil.apk")
+
+ URL(url).download(target) { progress, fileSize ->
+ builder.setProgress(fileSize.toInt(), progress.toInt(), false)
+ notificationManager.notify(UPDATE_NOTIFICATION_ID, builder.build())
+ }
+
+ val install = Intent(Intent.ACTION_VIEW).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
+ setDataAndType(FileProvider.getUriForFile(
+ context,
+ context.applicationContext.packageName + ".fileprovider",
+ target
+ ), MimeTypeMap.getSingleton().getExtensionFromMimeType(".apk"))
+ }
+
+ builder.apply {
+ setContentIntent(PendingIntent.getActivity(context, 0, install, 0))
+ setProgress(0, 0, false)
+ setSmallIcon(android.R.drawable.stat_sys_download_done)
+ setContentTitle(context.getString(R.string.update_download_completed))
+ setContentText(context.getString(R.string.update_download_completed_description))
+ }
+
+ notificationManager.cancel(UPDATE_NOTIFICATION_ID)
+
+ if (context.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED))
+ context.startActivity(install)
+ else
+ notificationManager.notify(UPDATE_NOTIFICATION_ID, builder.build())
+ }
+ }
+ setNegativeButton(R.string.ignore_update) { _, _ ->
+ preferences.edit()
+ .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
+ .apply()
+ }
+ }
+
+ launch(Dispatchers.Main) {
+ dialog.show()
+ }
}
}
diff --git a/app/src/main/res/layout/dialog_dl_location.xml b/app/src/main/res/layout/dialog_dl_location.xml
new file mode 100644
index 00000000..fde6c7d9
--- /dev/null
+++ b/app/src/main/res/layout/dialog_dl_location.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_dl_location.xml b/app/src/main/res/layout/item_dl_location.xml
new file mode 100644
index 00000000..50335e91
--- /dev/null
+++ b/app/src/main/res/layout/item_dl_location.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 3614852e..625fcd2b 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -9,7 +9,7 @@
ギャラリー検索
キャッシュクリア
キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?
- サイズ: %1$d%2$s
+ サイズ: %s
デフォルトキーワード
一回にロードするギャラリー数
検索設定
@@ -110,4 +110,10 @@
確認
復元に失敗しました
%1$d項目を復元しました
+ ダウンロード場所
+ 内部ストレージ
+ 外部SDカード
+ %s 使用可能
+ ダウンロードが完了しました
+ ここをクリックしてアップデートを行えます
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index e93e36cf..5c2ecdde 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -8,7 +8,7 @@
기본 검색어
캐시 정리하기
캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?
- 사용량: %1$d%2$s
+ 사용량: %s
한 번에 로드할 갤러리 수
검색 설정
설정
@@ -110,4 +110,10 @@
확인
복원에 실패했습니다
%1$d개 항목을 복원했습니다
+ 다운로드 위치
+ 내부 저장공간
+ 외부 SD카드
+ %s 사용 가능
+ 다운로드가 완료되었습니다
+ 여기를 클릭해서 업데이트를 진행할 수 있습니다
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5f695d13..afd7d1fd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -76,7 +76,9 @@
Update available
Download started
- Downloading apk…
+ Download Completed
+ Click here to update
+ Downloading update…
# Release Note(v%1$s)\n%2$s
Search galleries
@@ -96,6 +98,8 @@
Type: %1$s
Language: %1$s
+
+
Loading
Go to page
Fullscreen
@@ -107,22 +111,38 @@
Help
+
+
Settings
+
App version
Search Settings
Galleries per page
Default query
+
+
+
Storage
Clear cache
Deleting cache can affect image loading speed. Do you want to continue?
- Currently using %1$d%2$s
+ Currently using %s
Clear downloads
Delete all downloaded galleries.\nDo you want to continue?
Clear history
Do you want to clear histories?
%1$d histories saved
+ Download directory
+ Removable Storage
+ Internal Storage
+ %s available
+
+
+
App lock
App lock type
+
+
+
Miscellaneous
Use hiyobi.me
Load images from hiyobi.me to improve loading speed (if available)
@@ -139,6 +159,8 @@
Restore failed
%1$d entries restored
+
+
None
Pattern
PIN
@@ -150,6 +172,8 @@
Do you want to remove lock?
Lock is different from last one. Please try again.
+
+
Set default query
Language:
Filter BL
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index 244a015f..4cf82f4e 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -39,6 +39,10 @@
app:title="@string/settings_clear_history"
app:key="clear_history"/>
+
+
+ println("%.1f%%".format(downloaded*100.0/fileSize))
+ }
}
}