This commit is contained in:
Pablo González 2023-05-13 15:28:59 +01:00 committed by GitHub
commit ba691d1c90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 211 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,162 @@
/*
* 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.text.Html
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity
import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.badge.BadgeDrawable.BOTTOM_END
import com.google.android.material.badge.BadgeUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import org.json.JSONTokener
import java.io.File
import java.io.IOException
import java.net.URL
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, context.getString(R.string.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)
}
companion object {
private const val baseUrl = "https://skyline-builds.alula.gay"
private const val branch = "ftx1"
fun checkForUpdates(applicationContext : Context) {
val builder = AlertDialog.Builder(applicationContext)
CoroutineScope(Dispatchers.IO).launch {
val newestBuild = checkRemoteForUpdates()
if (newestBuild != null) {
val commit = newestBuild.getJSONObject("commit")
val remoteBuildGitHash = commit.getString("id")
if (BuildConfig.GIT_HASH != remoteBuildGitHash) {
val id = newestBuild.get("id")
val apkName = newestBuild.get("apkName")
val uri = Uri.parse("$baseUrl/cache/${id}/${apkName}")
val message = commit.getString("message")
var changelog = "<b>${applicationContext.getString(R.string.changelog)}</b><p>${message.substringBefore("\n")}</p>"
if (message.contains("\n"))
changelog += "<p>${message.substringAfter("\n")}</p>"
withContext(Dispatchers.Main) {
builder.setTitle("${applicationContext.getString(R.string.new_version)} ${newestBuild.get("runNumber")}")
.setMessage(Html.fromHtml(changelog, 0))
.setCancelable(true)
.setPositiveButton(applicationContext.getString(R.string.update)) { dialogInterface, _ ->
val receiver = AppUpdater()
applicationContext.registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
receiver.downloadApk(applicationContext, uri)
dialogInterface.dismiss()
}.setNegativeButton(applicationContext.getString(R.string.cancel)){ dialogInterface, _ ->
dialogInterface.cancel()
}.show()
}
} else {
withContext(Dispatchers.Main) {
builder.setTitle(applicationContext.getString(R.string.no_updates_available))
.setMessage(Html.fromHtml("<b>${applicationContext.getString(R.string.changelog)}</b><p>${commit.getString("message")}</p>", 0))
.setCancelable(true)
.setPositiveButton(applicationContext.getString(R.string.close)) { dialogInterface, _ ->
dialogInterface.dismiss()
}.show()
}
}
}
}
}
@com.google.android.material.badge.ExperimentalBadgeUtils
fun notifyUpdateBadge(context : Context, icon : ImageView) {
CoroutineScope(Dispatchers.IO).launch {
val newestBuild = checkRemoteForUpdates()
if (newestBuild != null) {
val remoteBuildGitHash = newestBuild.getJSONObject("commit").getString("id")
if (BuildConfig.GIT_HASH != remoteBuildGitHash) {
val badge = BadgeDrawable.create(context)
badge.badgeGravity = BOTTOM_END
badge.verticalOffset = 25
badge.horizontalOffset = 25
badge.backgroundColor = ContextCompat.getColor(context, R.color.colorPrimary)
BadgeUtils.attachBadgeDrawable(badge, icon)
}
}
}
}
private fun checkRemoteForUpdates() : JSONObject? {
val url = URL("$baseUrl/builds")
try {
val response = url.readText()
val jsonBuilds = JSONTokener(response).nextValue() as JSONArray
var ftx1Index = 0
while (ftx1Index < jsonBuilds.length() && jsonBuilds.getJSONObject(ftx1Index).get("branch") != branch) {
ftx1Index++
}
if (ftx1Index >= jsonBuilds.length())
ftx1Index = 0
return jsonBuilds.getJSONObject(ftx1Index)
} catch (e : IOException) {
e.printStackTrace()
return null
}
}
fun removeApk(){
if (File(SkylineApplication.instance.getPublicFilesDir().canonicalPath + "/skyline.apk").exists()) {
File(SkylineApplication.instance.getPublicFilesDir().canonicalPath + "/skyline.apk").delete()
}
}
}
}

View File

@ -291,6 +291,18 @@ class MainActivity : AppCompatActivity() {
}
}
})
binding.checkUpdatesIcon.apply {
if (BuildConfig.FLAVOR == "edge") {
visibility = View.GONE
} else {
AppUpdater.removeApk()
AppUpdater.notifyUpdateBadge(context, this)
this.setOnClickListener {
AppUpdater.checkForUpdates(context)
}
}
}
}
override fun onResume() {

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="?attr/colorOnSecondary" 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="?attr/colorOnBackground"
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

@ -10,9 +10,17 @@
<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>
<!-- Updater -->
<string name="new_version">New version</string>
<string name="changelog">Changelog</string>
<string name="update">Update</string>
<string name="download_completed">Download completed</string>
<string name="no_updates_available">No updates available</string>
<string name="close">Close</string>
<!-- Main -->
<string name="all">All</string>
<string name="metadata_missing">Metadata Missing</string>