Add an in-app updater

This commit is contained in:
PabloG02 2023-01-10 17:01:47 +01:00
parent cb62e15748
commit b2de510b2b
7 changed files with 156 additions and 1 deletions

View File

@ -18,6 +18,7 @@ android {
compileSdk 33
var isBuildSigned = (System.getenv("CI") == "true") && (System.getenv("IS_SKYLINE_SIGNED") == "true")
var gitCommitHash = 'git rev-parse --verify HEAD'.execute().text.trim()
defaultConfig {
applicationId "skyline.emu"
@ -28,6 +29,8 @@ android {
versionCode 3
versionName "0.0.3"
buildConfigField("String", "GIT_HASH", "\"${gitCommitHash}\"")
ndk {
//noinspection ChromeOsAbiSupport
abiFilters "arm64-v8a"

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-feature
android:name="android.hardware.vulkan.version"

View File

@ -0,0 +1,123 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.text.Html
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.startActivity
import org.json.JSONArray
import org.json.JSONTokener
import java.io.File
import java.io.IOException
import java.net.URL
import java.util.concurrent.Executors
class AppUpdater : BroadcastReceiver() {
private var downloadID = 0L
override fun onReceive(context : Context, intent : Intent) {
//Fetching the download id received with the broadcast
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
//Checking if the received broadcast is for our enqueued download by matching download id
if (downloadID == id) {
Toast.makeText(context, "Download Completed", Toast.LENGTH_SHORT).show()
val intentInstall = Intent(Intent.ACTION_INSTALL_PACKAGE)
intentInstall.data = (context.getSystemService(AppCompatActivity.DOWNLOAD_SERVICE) as DownloadManager).getUriForDownloadedFile(downloadID)
intentInstall.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
startActivity(context, intentInstall, null)
context.unregisterReceiver(this)
}
}
fun downloadApk(applicationContext : Context, uri : Uri){
val downloadPath = applicationContext.getPublicFilesDir().canonicalPath
val file = File(downloadPath, "skyline.apk")
if (File(file.path).exists()) {
File(file.path).delete()
}
val request = DownloadManager.Request(uri).setTitle("skyline")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
.setDestinationUri(Uri.fromFile(file))
val downloadManager = applicationContext.getSystemService(AppCompatActivity.DOWNLOAD_SERVICE) as DownloadManager
downloadID = downloadManager.enqueue(request)
}
init {
if (File(SkylineApplication.instance.getPublicFilesDir().canonicalPath+"/skyline.apk").exists()) {
File(SkylineApplication.instance.getPublicFilesDir().canonicalPath+"/skyline.apk").delete()
}
}
companion object {
private const val baseUrl = "https://skyline-builds.alula.gay"
private const val branch = "ftx1"
@JvmStatic
fun checkForUpdates(applicationContext : Context) {
val myExecutor = Executors.newSingleThreadExecutor()
val myHandler = Handler(Looper.getMainLooper())
val builder = AlertDialog.Builder(applicationContext)
val url = URL("$baseUrl/builds")
myExecutor.execute {
try {
val response = url.readText()
val jsonBuilds = JSONTokener(response).nextValue() as JSONArray
var ftx1Index = 0
while(ftx1Index < jsonBuilds.length() && !jsonBuilds.getJSONObject(ftx1Index).get("branch").equals(branch)){
ftx1Index++
}
if(ftx1Index >= jsonBuilds.length())
ftx1Index = 0
val remoteBuildGitHash = jsonBuilds.getJSONObject(ftx1Index).getJSONObject("commit").getString("id")
if (!BuildConfig.GIT_HASH.equals(remoteBuildGitHash)) {
val id = jsonBuilds.getJSONObject(ftx1Index).get("id")
val apkName = jsonBuilds.getJSONObject(ftx1Index).get("apkName")
val uri = Uri.parse("$baseUrl/cache/${id}/${apkName}")
myHandler.post {
builder.setTitle("New version ${jsonBuilds.getJSONObject(ftx1Index).get("runNumber")}")
.setMessage(Html.fromHtml("<b>Changelog</b><p>${jsonBuilds.getJSONObject(ftx1Index).getJSONObject("commit").getString("message")}</p>", 0))
.setCancelable(true)
.setPositiveButton("Update") { dialogInterface, it ->
val receiver = AppUpdater()
applicationContext.registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
receiver.downloadApk(applicationContext, uri)
dialogInterface.dismiss()
}.show()
}
} else {
myHandler.post {
builder.setTitle("No updates available")
.setMessage(Html.fromHtml("<b>Changelog</b><p>${jsonBuilds.getJSONObject(ftx1Index).getJSONObject("commit").getString("message")}</p>", 0))
.setCancelable(true)
.setPositiveButton("Close") { dialogInterface, it ->
dialogInterface.dismiss()
}.show()
}
}
} catch (e : IOException) {
e.printStackTrace()
}
}
}
}
}

View File

@ -151,6 +151,9 @@ class MainActivity : AppCompatActivity() {
Snackbar.make(this@MainActivity.findViewById(android.R.id.content), getString(R.string.logs_not_found), Snackbar.LENGTH_SHORT).show()
}
}
binding.checkUpdatesIcon.setOnClickListener {
AppUpdater.checkForUpdates(context)
}
binding.settingsIcon.setOnClickListener { settingsCallback.launch(Intent(context, SettingsActivity::class.java)) }
binding.refreshIcon.setOnClickListener { loadRoms(false) }
addTextChangedListener(afterTextChanged = { editable ->

View File

@ -0,0 +1,8 @@
<!-- drawable/download.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</vector>

View File

@ -40,12 +40,28 @@
android:padding="5dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@id/sub_text"
app:layout_constraintEnd_toStartOf="@id/log_icon"
app:layout_constraintEnd_toStartOf="@id/check_updates_icon"
app:layout_constraintTop_toTopOf="@id/title_text"
app:srcCompat="@drawable/ic_refresh"
app:tint="?android:attr/textColorSecondary"
tools:visibility="visible" />
<ImageView
android:id="@+id/check_updates_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/check_updates"
android:padding="5dp"
app:layout_constraintBottom_toBottomOf="@id/sub_text"
app:layout_constraintEnd_toStartOf="@id/log_icon"
app:layout_constraintTop_toTopOf="@id/title_text"
app:srcCompat="@drawable/ic_check_updates"
app:tint="?android:attr/textColorSecondary"
tools:visibility="visible" />
<ImageView
android:id="@+id/log_icon"
android:layout_width="30dp"

View File

@ -8,6 +8,7 @@
<string name="settings">Settings</string>
<string name="share_logs">Share Logs</string>
<string name="refresh">Refresh</string>
<string name="check_updates">Check for updates</string>
<!-- Toolbar - Share Logs -->
<string name="log_share_prompt">Share log file</string>
<string name="logs_not_found">No logs were created during the last run</string>