From abd2f3ae17197b61c4f664946bb129f3d7fd0d07 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 12 May 2019 19:15:53 +0900 Subject: [PATCH] Added update feature --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 13 +++ .../java/xyz/quaver/pupil/MainActivity.kt | 94 +++++++++++++++++++ .../java/xyz/quaver/pupil/SettingsActivity.kt | 4 +- .../main/java/xyz/quaver/pupil/util/update.kt | 31 ++++++ app/src/main/res/values/strings.xml | 12 ++- app/src/main/res/xml/filepaths.xml | 4 + .../java/xyz/quaver/pupil/ExampleUnitTest.kt | 8 ++ build.gradle | 2 +- libpupil/build.gradle | 3 +- .../main/java/xyz/quaver/hitomi/readers.kt | 9 +- 11 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/util/update.kt create mode 100644 app/src/main/res/xml/filepaths.xml diff --git a/app/build.gradle b/app/build.gradle index bf27adc7..9f4d114b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae7692db..1bdffc64 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ package="xyz.quaver.pupil"> + + + + + + + >() 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 { diff --git a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt index f179742e..3df30a5a 100644 --- a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt @@ -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()) diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt new file mode 100644 index 00000000..e5037094 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -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? { + 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 +} \ 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 f8f2a91e..ea030bd9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,17 @@ + Warning + Pupil + Denying any permission can deactivate some functions + + https://api.github.com/repos/tom5079/Pupil-issue/releases + Pupil-v(\\d+\\.)+\\d+\\.apk + + Update available + Version %1$s is available!\n(current version is %2$s)\nDo you want to update? + Downloading apk… + Settings Search No result @@ -26,7 +37,6 @@ Delete Cache Currently using %1$d%2$s of cache - Warning Deleting cache can affect image loading speed. Do you want to continue? diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml new file mode 100644 index 00000000..39d4696d --- /dev/null +++ b/app/src/main/res/xml/filepaths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt index d5e91efa..763e19e4 100644 --- a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt +++ b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt @@ -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")) + } + } diff --git a/build.gradle b/build.gradle index c136f825..f65a220a 100644 --- a/build.gradle +++ b/build.gradle @@ -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 } diff --git a/libpupil/build.gradle b/libpupil/build.gradle index 17a6560d..b616a77e 100644 --- a/libpupil/build.gradle +++ b/libpupil/build.gradle @@ -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" } diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt index 23d43b58..8ceb06f9 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt @@ -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> ) //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.addAll(Json.parseList( - Regex("""\[.+\]""").find( + galleryInfo.addAll( + Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, + Regex("""\[.+]""").find( URL(galleryInfoUrl).readText() )?.value ?: "[]" )