Added update feature
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
@@ -22,9 +23,10 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.preference:preference:1.1.0-alpha05'
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
package="xyz.quaver.pupil">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -12,6 +14,17 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="xyz.quaver.pupil.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths"/>
|
||||
</provider>
|
||||
|
||||
<activity android:name=".GalleryActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"/>
|
||||
<activity
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
package xyz.quaver.pupil
|
||||
|
||||
import android.Manifest
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.*
|
||||
import android.text.style.AlignmentSpan
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -22,10 +35,14 @@ import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||
import xyz.quaver.pupil.types.TagSuggestion
|
||||
import xyz.quaver.pupil.util.SetLineOverlap
|
||||
import xyz.quaver.pupil.util.checkUpdate
|
||||
import xyz.quaver.pupil.util.getApkUrl
|
||||
import java.io.File
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private val PERMISSION_REQUEST_CODE = 4585
|
||||
private val galleries = ArrayList<Pair<GalleryBlock, Bitmap?>>()
|
||||
|
||||
private var isLoading = false
|
||||
@@ -35,6 +52,10 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
checkPermission()
|
||||
|
||||
update()
|
||||
|
||||
main_appbar_layout.addOnOffsetChangedListener(
|
||||
AppBarLayout.OnOffsetChangedListener { _, p1 ->
|
||||
main_searchview.translationY = p1.toFloat()
|
||||
@@ -58,6 +79,79 @@ class MainActivity : AppCompatActivity() {
|
||||
fetchGalleries(query)
|
||||
}
|
||||
|
||||
private fun checkPermission() {
|
||||
val permissions = arrayOf(
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
|
||||
if (permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) {
|
||||
if (permissions.any { ActivityCompat.shouldShowRequestPermissionRationale(this, it) })
|
||||
AlertDialog.Builder(this).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.permission_explain)
|
||||
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
}.show()
|
||||
else
|
||||
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun update() {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
||||
return
|
||||
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val update =
|
||||
checkUpdate(getString(R.string.release_url), BuildConfig.VERSION_NAME) ?: return@launch
|
||||
|
||||
val (url, fileName) = getApkUrl(update, getString(R.string.release_name)) ?: return@launch
|
||||
|
||||
val dialog = AlertDialog.Builder(this@MainActivity).apply {
|
||||
setTitle(R.string.update_title)
|
||||
setMessage(getString(R.string.update_message, update["tag_name"], BuildConfig.VERSION_NAME))
|
||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
val dest = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
|
||||
val desturi =
|
||||
FileProvider.getUriForFile(
|
||||
applicationContext,
|
||||
"xyz.quaver.pupil.provider",
|
||||
dest
|
||||
)
|
||||
|
||||
if (dest.exists())
|
||||
dest.delete()
|
||||
|
||||
val request = DownloadManager.Request(Uri.parse(url)).apply {
|
||||
setDescription(getString(R.string.update_notification_description))
|
||||
setTitle(getString(R.string.app_name))
|
||||
setDestinationUri(Uri.fromFile(dest))
|
||||
}
|
||||
|
||||
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val id = manager.enqueue(request)
|
||||
|
||||
registerReceiver(object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val install = Intent(Intent.ACTION_VIEW).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
setDataAndType(desturi, manager.getMimeTypeForDownloadedFile(id))
|
||||
}
|
||||
|
||||
startActivity(install)
|
||||
unregisterReceiver(this)
|
||||
finish()
|
||||
}
|
||||
}, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
}
|
||||
setNegativeButton(android.R.string.no) { _, _ ->}
|
||||
}
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
with(main_recyclerview) {
|
||||
adapter = GalleryBlockAdapter(galleries).apply {
|
||||
|
||||
@@ -51,8 +51,8 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(getString(R.string.settings_delete_cache_alert_title))
|
||||
setMessage(getString(R.string.settings_delete_cache_alert_message))
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_delete_cache_alert_message)
|
||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
with(context.cacheDir) {
|
||||
if (exists())
|
||||
|
||||
31
app/src/main/java/xyz/quaver/pupil/util/update.kt
Normal file
31
app/src/main/java/xyz/quaver/pupil/util/update.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package xyz.quaver.pupil.util
|
||||
|
||||
import kotlinx.serialization.json.*
|
||||
import java.net.URL
|
||||
|
||||
fun getReleases(url: String) : JsonArray {
|
||||
return URL(url).readText().let {
|
||||
Json(JsonConfiguration.Stable).parse(JsonArray.serializer(), it)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkUpdate(url: String, currentVersion: String) : JsonObject? {
|
||||
val releases = getReleases(url)
|
||||
|
||||
if (releases.isEmpty())
|
||||
return null
|
||||
|
||||
if (currentVersion != releases[0].jsonObject["tag_name"]?.content)
|
||||
return releases[0].jsonObject
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getApkUrl(releases: JsonObject, releaseName: String) : Pair<String?, String?>? {
|
||||
releases["assets"]?.jsonArray?.forEach {
|
||||
if (Regex(releaseName).matches(it.jsonObject["name"]?.content ?: ""))
|
||||
return Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
<resources>
|
||||
<string name="warning">Warning</string>
|
||||
|
||||
<string name="app_name">Pupil</string>
|
||||
|
||||
<string name="permission_explain">Denying any permission can deactivate some functions</string>
|
||||
|
||||
<string name="release_url">https://api.github.com/repos/tom5079/Pupil-issue/releases</string>
|
||||
<string name="release_name">Pupil-v(\\d+\\.)+\\d+\\.apk</string>
|
||||
|
||||
<string name="update_title">Update available</string>
|
||||
<string name="update_message">Version %1$s is available!\n(current version is %2$s)\nDo you want to update?</string>
|
||||
<string name="update_notification_description">Downloading apk…</string>
|
||||
|
||||
<string name="main_settings">Settings</string>
|
||||
<string name="main_search">Search</string>
|
||||
<string name="main_no_result">No result</string>
|
||||
@@ -26,7 +37,6 @@
|
||||
|
||||
<string name="settings_delete_cache">Delete Cache</string>
|
||||
<string name="settings_delete_cache_summary">Currently using %1$d%2$s of cache</string>
|
||||
<string name="settings_delete_cache_alert_title">Warning</string>
|
||||
<string name="settings_delete_cache_alert_message">Deleting cache can affect image loading speed. Do you want to continue?</string>
|
||||
|
||||
</resources>
|
||||
|
||||
4
app/src/main/res/xml/filepaths.xml
Normal file
4
app/src/main/res/xml/filepaths.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path name="Download" path="Download"/>
|
||||
</paths>
|
||||
@@ -1,5 +1,8 @@
|
||||
package xyz.quaver.pupil
|
||||
|
||||
import org.junit.Test
|
||||
import xyz.quaver.pupil.util.checkUpdate
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
@@ -8,4 +11,9 @@ package xyz.quaver.pupil
|
||||
|
||||
class ExampleUnitTest {
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
print(checkUpdate("https://api.github.com/repos/tom5079/Pupil-issue/releases", "0.0.1"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apply plugin: 'kotlinx-serialization'
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
|
||||
implementation 'org.jsoup:jsoup:1.11.3'
|
||||
@@ -19,7 +19,6 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package xyz.quaver.hitomi
|
||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.list
|
||||
import kotlinx.serialization.parseList
|
||||
import org.jsoup.Jsoup
|
||||
import java.net.URL
|
||||
@@ -21,7 +23,6 @@ data class Reader(
|
||||
val images: List<Pair<URL, GalleryInfo?>>
|
||||
)
|
||||
//Set header `Referer` to reader url to avoid 403 error
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
fun getReader(galleryID: Int) : Reader {
|
||||
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
|
||||
val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js"
|
||||
@@ -36,8 +37,10 @@ fun getReader(galleryID: Int) : Reader {
|
||||
|
||||
val galleryInfo = ArrayList<GalleryInfo?>()
|
||||
|
||||
galleryInfo.addAll(Json.parseList(
|
||||
Regex("""\[.+\]""").find(
|
||||
galleryInfo.addAll(
|
||||
Json(JsonConfiguration.Stable).parse(
|
||||
GalleryInfo.serializer().list,
|
||||
Regex("""\[.+]""").find(
|
||||
URL(galleryInfoUrl).readText()
|
||||
)?.value ?: "[]"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user