From b3d71518577349c65db68ec997bf2b4b4e9524a9 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Sat, 1 Feb 2025 19:40:48 +0530 Subject: [PATCH 01/10] Add required network dependencies --- app/build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 01b8f2fea..45ce761e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -167,6 +167,14 @@ dependencies { implementation 'info.debatty:java-string-similarity:2.0.0' implementation 'com.github.KikiManjaro:colorpicker:v1.1.12' implementation 'com.github.android:renderscript-intrinsics-replacement-toolkit:344be3f' + + /* Network */ + implementation 'io.ktor:ktor-client-core:3.0.3' + implementation 'io.ktor:ktor-client-cio:3.0.3' + implementation 'io.ktor:ktor-client-json:3.0.3' + implementation 'io.ktor:ktor-serialization-kotlinx-json:3.0.3' + implementation 'io.ktor:ktor-client-content-negotiation:3.0.3' + implementation 'io.ktor:ktor-client-logging:3.0.3' } kapt { From b1a9f34a3ffe6a3e934e5745527ce0b9e5465779 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Sat, 1 Feb 2025 19:43:17 +0530 Subject: [PATCH 02/10] Implement DriversFetcher.kt --- .../stratoemu/strato/utils/DriversFetcher.kt | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt diff --git a/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt b/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt new file mode 100644 index 000000000..ef5c6b7c2 --- /dev/null +++ b/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt @@ -0,0 +1,105 @@ +package org.stratoemu.strato.utils + +import android.content.Context +import android.net.Uri +import android.util.Log +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.serialization.kotlinx.json.* +import io.ktor.utils.io.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import java.io.OutputStream +import java.io.FileOutputStream +import java.io.File + +object DriversFetcher { + private val httpClient = HttpClient { + install(ContentNegotiation) { + json(Json { ignoreUnknownKeys = true }) + } + install(Logging) { + level = LogLevel.BODY + } + } + + @Serializable + data class GitHubRelease( + val name: String, + val assets: List = emptyList() + ) + + @Serializable + data class Asset(val browser_download_url: String) + + suspend fun fetchReleases(repoUrl: String): List> { + val repoPath = repoUrl.removePrefix("https://github.com/") + val validationUrl = "https://api.github.com/repos/$repoPath/contents/.adrenoDrivers" + val apiUrl = "https://api.github.com/repos/$repoPath/releases" + + return try { + val isValid = withContext(Dispatchers.IO) { + try { + httpClient.get(validationUrl).status.value == 200 + } catch (e: Exception) { + false + } + } + + if (!isValid) { + Log.d("DriversFetcher", "Provided driver repo url is not valid.") + return emptyList() + } + + val releases: List = withContext(Dispatchers.IO) { + httpClient.get(apiUrl).body() + } + releases.map { release -> + val assetUrl = release.assets.firstOrNull()?.browser_download_url + release.name to assetUrl + } + } catch (e: Exception) { + Log.e("DriversFetcher", "Error fetching releases: ${e.message}", e) + emptyList() + } + } + + suspend fun downloadAsset(assetUrl: String, destinationFile: File): DownloadResult { + return try { + withContext(Dispatchers.IO) { + val response: HttpResponse = httpClient.get(assetUrl) + FileOutputStream(destinationFile)?.use { outputStream -> + writeResponseToStream(response, outputStream) + } ?: return@withContext DownloadResult.Error("Failed to open ${destinationFile.absolutePath.toString()}") + } + DownloadResult.Success + } catch (e: Exception) { + Log.e("DriversFetcher", "Error downloading file: ${e.message}", e) + DownloadResult.Error(e.message) + } + } + + private suspend fun writeResponseToStream(response: HttpResponse, outputStream: OutputStream) { + val channel = response.bodyAsChannel() + val buffer = ByteArray(8192) // 8KB buffer size + + while (!channel.isClosedForRead) { + val bytesRead = channel.readAvailable(buffer) + if (bytesRead > 0) { + outputStream.write(buffer, 0, bytesRead) + } + } + outputStream.flush() + } + + sealed class DownloadResult { + object Success : DownloadResult() + data class Error(val message: String?) : DownloadResult() + } +} From bdfbb012edee8e0645ab9c55395a90fdcf948238 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Sat, 1 Feb 2025 19:52:18 +0530 Subject: [PATCH 03/10] GpuDriverActivity: Implement importing option --- .../strato/preference/GpuDriverActivity.kt | 103 +++++++++++++++++- 1 file changed, 98 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt index 46e6b83d0..c7b3b2bdd 100644 --- a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt +++ b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt @@ -9,13 +9,16 @@ import android.content.Intent import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.os.Bundle import android.view.ViewTreeObserver +import android.view.LayoutInflater import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.WindowCompat +import androidx.core.widget.doOnTextChanged import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewbinding.ViewBinding import com.google.android.material.snackbar.Snackbar +import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.stratoemu.strato.R import org.stratoemu.strato.adapter.GenericListItem @@ -30,9 +33,16 @@ import org.stratoemu.strato.utils.GpuDriverHelper import org.stratoemu.strato.utils.GpuDriverInstallResult import org.stratoemu.strato.utils.WindowInsetsHelper import org.stratoemu.strato.utils.serializable +import org.stratoemu.strato.utils.DriversFetcher +import org.stratoemu.strato.utils.DriversFetcher.DownloadResult +import org.stratoemu.strato.StratoApplication +import org.stratoemu.strato.getPublicFilesDir import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.io.File +import java.io.FileInputStream /** * This activity is used to manage the installed gpu drivers and select one to use. @@ -182,16 +192,99 @@ class GpuDriverActivity : AppCompatActivity() { binding.driverList.addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelSize(R.dimen.grid_padding))) binding.addDriverButton.setOnClickListener { - val intent = Intent(Intent.ACTION_GET_CONTENT).apply { - addFlags(FLAG_GRANT_READ_URI_PERMISSION) - type = "application/zip" - } - installCallback.launch(intent) + val items = arrayOf(getString(R.string.driver_import), getString(R.string.install)) + var checkedItem = 0 + var selectedItem: String? = items[0] + + MaterialAlertDialogBuilder(this) + .setTitle(R.string.choose) + .setSingleChoiceItems(items, checkedItem) { dialog, which -> + selectedItem = items[which] + } + .setPositiveButton(android.R.string.ok) { dialog, _ -> + if (selectedItem == getString(R.string.install)) { + val intent = Intent(Intent.ACTION_GET_CONTENT).apply { + addFlags(FLAG_GRANT_READ_URI_PERMISSION) + type = "application/zip" + } + installCallback.launch(intent) + } else { + handleGpuDriverImport() + } + } + .setNegativeButton(android.R.string.cancel, null) + .show() } populateAdapter() } + private fun handleGpuDriverImport() { + val inflater = LayoutInflater.from(this) + val inputBinding = DialogKeyboardBinding.inflate(inflater) + var textInputValue: String = getString(R.string.default_driver_repo_url) + + inputBinding.editTextInput.setText(textInputValue) + inputBinding.editTextInput.doOnTextChanged { text, _, _, _ -> + textInputValue = text.toString() + } + + MaterialAlertDialogBuilder(this) + .setView(inputBinding.root) + .setTitle(R.string.enter_repo_url) + .setPositiveButton(R.string.fetch) { _, _ -> + if (textInputValue.isNotEmpty()) { + fetchAndShowDrivers(textInputValue) + } + } + .setNegativeButton(android.R.string.cancel) {_, _ -> } + .show() + } + + private fun fetchAndShowDrivers(repoUrl: String) { + lifecycleScope.launch(Dispatchers.Main) { + val releases = DriversFetcher.fetchReleases(repoUrl) + if (releases.isEmpty()) { + Snackbar.make(binding.root, "Failed to fetch ${repoUrl}: validation failed or check your internet connection", Snackbar.LENGTH_SHORT).show() + return@launch + } + + val releaseNames = releases.map { it.first } + val releaseUrls = releases.map { it.second } + var chosenUrl: String? = releaseUrls[0] + var chosenName: String? = releaseNames[0] + + MaterialAlertDialogBuilder(this@GpuDriverActivity) + .setTitle(R.string.drivers) + .setSingleChoiceItems(releaseNames.toTypedArray(), 0) { _, which -> + chosenUrl = releaseUrls[which] + chosenName = releaseNames[which] + } + .setPositiveButton(R.string.driver_import) { _, _ -> + downloadDriver(chosenUrl!!, chosenName!!) + } + .setNegativeButton(android.R.string.cancel, null) + .show() + } + } + + private fun downloadDriver(chosenUrl: String, chosenName: String) { + GlobalScope.launch(Dispatchers.Main) { + var driverFile = File("${StratoApplication.instance.getPublicFilesDir().canonicalPath}/${chosenName}.zip") + if (!driverFile.exists()) driverFile.createNewFile() + val result = DriversFetcher.downloadAsset(chosenUrl!!, driverFile) + when (result) { + is DownloadResult.Success -> { + val result = GpuDriverHelper.installDriver(this@GpuDriverActivity, FileInputStream(driverFile)) + Snackbar.make(binding.root, resolveInstallResultString(result), Snackbar.LENGTH_LONG).show() + if (result == GpuDriverInstallResult.Success) populateAdapter() + } + is DownloadResult.Error -> Snackbar.make(binding.root, "Failed to import ${chosenName}: ${result.message}", Snackbar.LENGTH_SHORT).show() + } + driverFile.delete() + } + } + private fun resolveInstallResultString(result : GpuDriverInstallResult) = when (result) { GpuDriverInstallResult.Success -> getString(R.string.gpu_driver_install_success) GpuDriverInstallResult.InvalidArchive -> getString(R.string.gpu_driver_install_invalid_archive) From e2a1bb8bd327cd4836bcbe76930b630430e6b08b Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Sat, 1 Feb 2025 19:54:34 +0530 Subject: [PATCH 04/10] Update kotlin to 2.0.10 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c190be961..f9f1253e9 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { - kotlin_version = '1.9.22' + kotlin_version = '2.0.10' hilt_version = '2.50' } From 283f0729adff659e2efd5f3d439b5d00dc807f0e Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:45:49 +0530 Subject: [PATCH 05/10] fix some errors due to updated kotlin version --- .../input/onscreen/OnScreenItemDefinitions.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/stratoemu/strato/input/onscreen/OnScreenItemDefinitions.kt b/app/src/main/java/org/stratoemu/strato/input/onscreen/OnScreenItemDefinitions.kt index d3c3ab518..c67bf4b05 100644 --- a/app/src/main/java/org/stratoemu/strato/input/onscreen/OnScreenItemDefinitions.kt +++ b/app/src/main/java/org/stratoemu/strato/input/onscreen/OnScreenItemDefinitions.kt @@ -26,6 +26,7 @@ import org.stratoemu.strato.utils.add import org.stratoemu.strato.utils.multiply import kotlin.math.roundToInt import com.google.android.material.R as MaterialR +import org.stratoemu.strato.StratoApplication open class CircularButton( onScreenControllerView : OnScreenControllerView, @@ -33,7 +34,7 @@ open class CircularButton( defaultRelativeX : Float, defaultRelativeY : Float, defaultRelativeRadiusToX : Float, - drawableId : Int = R.drawable.ic_button, + drawableId : Int = StratoApplication.context.resources.getIdentifier("ic_button", "drawable", StratoApplication.context.packageName), defaultEnabled : Boolean = true ) : OnScreenButton( onScreenControllerView, @@ -65,9 +66,9 @@ open class JoystickButton( defaultRelativeX, defaultRelativeY, defaultRelativeRadiusToX, - R.drawable.ic_button + StratoApplication.context.resources.getIdentifier("ic_button", "drawable", StratoApplication.context.packageName) ) { - private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick) + private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, StratoApplication.context.resources.getIdentifier("ic_stick", "drawable", StratoApplication.context.packageName)) open var recenterSticks = false set(value) { @@ -275,7 +276,7 @@ open class RectangularButton( defaultRelativeY : Float, defaultRelativeWidth : Float, defaultRelativeHeight : Float, - drawableId : Int = R.drawable.ic_rectangular_button, + drawableId : Int = StratoApplication.context.resources.getIdentifier("ic_rectangular_button", "drawable", StratoApplication.context.packageName), defaultEnabled : Boolean = true ) : OnScreenButton( onScreenControllerView, @@ -305,9 +306,9 @@ class TriggerButton( defaultRelativeWidth, defaultRelativeHeight, when (buttonId) { - ZL -> R.drawable.ic_trigger_button_left + ZL -> StratoApplication.context.resources.getIdentifier("ic_trigger_button_left", "drawable", StratoApplication.context.packageName) - ZR -> R.drawable.ic_trigger_button_right + ZR -> StratoApplication.context.resources.getIdentifier("ic_trigger_button_right", "drawable", StratoApplication.context.packageName) else -> error("Unsupported trigger button") } From 72971880d7bc8b049819a1739e0185e71b84c7f0 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:22:57 +0530 Subject: [PATCH 06/10] DriversFetcher: return realtime progress while downloading asset --- .../stratoemu/strato/utils/DriversFetcher.kt | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt b/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt index ef5c6b7c2..409632e5f 100644 --- a/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt +++ b/app/src/main/java/org/stratoemu/strato/utils/DriversFetcher.kt @@ -11,6 +11,7 @@ import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.serialization.kotlinx.json.* import io.ktor.utils.io.* +import io.ktor.http.HttpHeaders import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable @@ -38,12 +39,19 @@ object DriversFetcher { @Serializable data class Asset(val browser_download_url: String) - suspend fun fetchReleases(repoUrl: String): List> { + suspend fun fetchReleases(repoUrl: String, bypassValidation: Boolean = false): FetchResultOutput { val repoPath = repoUrl.removePrefix("https://github.com/") val validationUrl = "https://api.github.com/repos/$repoPath/contents/.adrenoDrivers" val apiUrl = "https://api.github.com/repos/$repoPath/releases" return try { + val response: HttpResponse = withContext(Dispatchers.IO) { + httpClient.get(apiUrl) + } + + if (response.status.value != 200) + return FetchResultOutput(emptyList(), FetchResult.Error("Failed to fetch drivers")) + val isValid = withContext(Dispatchers.IO) { try { httpClient.get(validationUrl).status.value == 200 @@ -52,31 +60,35 @@ object DriversFetcher { } } - if (!isValid) { - Log.d("DriversFetcher", "Provided driver repo url is not valid.") - return emptyList() + if (!isValid && !bypassValidation) { + return FetchResultOutput(emptyList(), FetchResult.Warning("Provided driver repo url is not valid.")) } - val releases: List = withContext(Dispatchers.IO) { - httpClient.get(apiUrl).body() - } - releases.map { release -> + val releases: List = response.body() + val drivers = releases.map { release -> val assetUrl = release.assets.firstOrNull()?.browser_download_url release.name to assetUrl } + FetchResultOutput(drivers, FetchResult.Success) } catch (e: Exception) { Log.e("DriversFetcher", "Error fetching releases: ${e.message}", e) - emptyList() + FetchResultOutput(emptyList(), FetchResult.Error("Error fetching releases: ${e.message}")) } } - suspend fun downloadAsset(assetUrl: String, destinationFile: File): DownloadResult { + suspend fun downloadAsset( + assetUrl: String, + destinationFile: File, + progressCallback: (Long, Long) -> Unit + ): DownloadResult { return try { withContext(Dispatchers.IO) { val response: HttpResponse = httpClient.get(assetUrl) + val contentLength = response.headers[HttpHeaders.ContentLength]?.toLong() ?: -1L + FileOutputStream(destinationFile)?.use { outputStream -> - writeResponseToStream(response, outputStream) - } ?: return@withContext DownloadResult.Error("Failed to open ${destinationFile.absolutePath.toString()}") + writeResponseToStream(response, outputStream, contentLength, progressCallback) + } ?: return@withContext DownloadResult.Error("Failed to open ${destinationFile.absolutePath}") } DownloadResult.Success } catch (e: Exception) { @@ -85,14 +97,22 @@ object DriversFetcher { } } - private suspend fun writeResponseToStream(response: HttpResponse, outputStream: OutputStream) { + private suspend fun writeResponseToStream( + response: HttpResponse, + outputStream: OutputStream, + contentLength: Long, + progressCallback: (Long, Long) -> Unit + ) { val channel = response.bodyAsChannel() - val buffer = ByteArray(8192) // 8KB buffer size + val buffer = ByteArray(1024) // 1KB buffer size + var totalBytesRead = 0L while (!channel.isClosedForRead) { val bytesRead = channel.readAvailable(buffer) if (bytesRead > 0) { outputStream.write(buffer, 0, bytesRead) + totalBytesRead += bytesRead + progressCallback(totalBytesRead, contentLength) } } outputStream.flush() @@ -102,4 +122,15 @@ object DriversFetcher { object Success : DownloadResult() data class Error(val message: String?) : DownloadResult() } + + data class FetchResultOutput( + val fetchedDrivers: List>, + val result: FetchResult + ) + + sealed class FetchResult { + object Success : FetchResult() + data class Error(val message: String?) : FetchResult() + data class Warning(val message: String?) : FetchResult() + } } From 37e72ca7b46f21cf3bdbbf3dcf2ca01ea0951fcf Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:29:40 +0530 Subject: [PATCH 07/10] GpuDriverActivity: show realtime downloading progress --- .../strato/preference/GpuDriverActivity.kt | 84 +++++++++++++++++-- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt index c7b3b2bdd..45ae7a11b 100644 --- a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt +++ b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt @@ -10,6 +10,8 @@ import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.os.Bundle import android.view.ViewTreeObserver import android.view.LayoutInflater +import android.view.View +import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.coordinatorlayout.widget.CoordinatorLayout @@ -19,6 +21,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewbinding.ViewBinding import com.google.android.material.snackbar.Snackbar import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.progressindicator.LinearProgressIndicator import dagger.hilt.android.AndroidEntryPoint import org.stratoemu.strato.R import org.stratoemu.strato.adapter.GenericListItem @@ -34,6 +37,8 @@ import org.stratoemu.strato.utils.GpuDriverInstallResult import org.stratoemu.strato.utils.WindowInsetsHelper import org.stratoemu.strato.utils.serializable import org.stratoemu.strato.utils.DriversFetcher +import org.stratoemu.strato.utils.DriversFetcher.FetchResult +import org.stratoemu.strato.utils.DriversFetcher.FetchResultOutput import org.stratoemu.strato.utils.DriversFetcher.DownloadResult import org.stratoemu.strato.StratoApplication import org.stratoemu.strato.getPublicFilesDir @@ -243,14 +248,32 @@ class GpuDriverActivity : AppCompatActivity() { private fun fetchAndShowDrivers(repoUrl: String) { lifecycleScope.launch(Dispatchers.Main) { - val releases = DriversFetcher.fetchReleases(repoUrl) - if (releases.isEmpty()) { - Snackbar.make(binding.root, "Failed to fetch ${repoUrl}: validation failed or check your internet connection", Snackbar.LENGTH_SHORT).show() + val progressDialog = MaterialAlertDialogBuilder(this@GpuDriverActivity) + .setTitle(R.string.fetching) + .setView(R.layout.dialog_progress_bar) + .setCancelable(false) + .create() + progressDialog.show() + val progressBar = progressDialog.findViewById(R.id.progress_bar) + val progressText = progressDialog.findViewById(R.id.progress_text) + progressText?.visibility = View.GONE + progressBar?.isIndeterminate = true + + val fetchOutput = DriversFetcher.fetchReleases(repoUrl, bypassValidation) + progressDialog.dismiss() + + if (fetchOutput.result is FetchResult.Error) { + showErrorDialog(fetchOutput.result.message ?: "Something unexpected occurred while fetching $repoUrl drivers") + return@launch + } + + if (fetchOutput.result is FetchResult.Warning) { + showWarningDialog(repoUrl, fetchOutput.result.message ?: "Something unexpected occurred while fetching $repoUrl drivers") return@launch } - val releaseNames = releases.map { it.first } - val releaseUrls = releases.map { it.second } + val releaseNames = fetchOutput.fetchedDrivers.map { it.first } + val releaseUrls = fetchOutput.fetchedDrivers.map { it.second } var chosenUrl: String? = releaseUrls[0] var chosenName: String? = releaseNames[0] @@ -270,9 +293,37 @@ class GpuDriverActivity : AppCompatActivity() { private fun downloadDriver(chosenUrl: String, chosenName: String) { GlobalScope.launch(Dispatchers.Main) { + val progressDialog = MaterialAlertDialogBuilder(this@GpuDriverActivity) + .setTitle(R.string.downloading) + .setView(R.layout.dialog_progress_bar) + .setCancelable(false) + .create() + + progressDialog.show() + val progressBar = progressDialog.findViewById(R.id.progress_bar) + val progressText = progressDialog.findViewById(R.id.progress_text) + progressText?.visibility = View.GONE + progressBar?.isIndeterminate = true + var driverFile = File("${StratoApplication.instance.getPublicFilesDir().canonicalPath}/${chosenName}.zip") if (!driverFile.exists()) driverFile.createNewFile() - val result = DriversFetcher.downloadAsset(chosenUrl!!, driverFile) + val result = DriversFetcher.downloadAsset(chosenUrl, driverFile) { downloadedBytes, totalBytes -> + // when using unit it stays to of this unit origin thread that's why we need to use main thread + GlobalScope.launch(Dispatchers.Main) { + if (totalBytes > 0) { + if (progressBar?.isIndeterminate ?: false) progressBar?.isIndeterminate = false + if (progressText?.visibility == View.GONE) progressText?.visibility = View.VISIBLE + val progress = (downloadedBytes * 100 / totalBytes).toInt() + progressBar?.max = 100 + progressBar?.progress = progress + progressText?.text = "$progress%" + } else { + if (progressText?.visibility == View.VISIBLE) progressText?.visibility = View.GONE + if (!(progressBar?.isIndeterminate ?: false)) progressBar?.isIndeterminate = true + } + } + } + progressDialog.dismiss() when (result) { is DownloadResult.Success -> { val result = GpuDriverHelper.installDriver(this@GpuDriverActivity, FileInputStream(driverFile)) @@ -285,6 +336,27 @@ class GpuDriverActivity : AppCompatActivity() { } } + private fun showErrorDialog(message: String) { + MaterialAlertDialogBuilder(this@GpuDriverActivity) + .setTitle(R.string.error) + .setMessage(message) + .setPositiveButton(R.string.close, null) + .create() + .show() + } + + private fun showWarningDialog(repoUrl: String, message: String) { + MaterialAlertDialogBuilder(this@GpuDriverActivity) + .setTitle(R.string.warning) + .setMessage(message) + .setPositiveButton(R.string.misc_continue) { _, _ -> + fetchAndShowDrivers(repoUrl, true) + } + .setNegativeButton(android.R.string.cancel, null) + .create() + .show() + } + private fun resolveInstallResultString(result : GpuDriverInstallResult) = when (result) { GpuDriverInstallResult.Success -> getString(R.string.gpu_driver_install_success) GpuDriverInstallResult.InvalidArchive -> getString(R.string.gpu_driver_install_invalid_archive) From 76def82381e3feb1a64ecb2ed08631a0d9f48010 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:32:18 +0530 Subject: [PATCH 08/10] Typo --- .../java/org/stratoemu/strato/preference/GpuDriverActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt index 45ae7a11b..e72bdbefc 100644 --- a/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt +++ b/app/src/main/java/org/stratoemu/strato/preference/GpuDriverActivity.kt @@ -246,7 +246,7 @@ class GpuDriverActivity : AppCompatActivity() { .show() } - private fun fetchAndShowDrivers(repoUrl: String) { + private fun fetchAndShowDrivers(repoUrl: String, bypassValidation: Boolean = false) { lifecycleScope.launch(Dispatchers.Main) { val progressDialog = MaterialAlertDialogBuilder(this@GpuDriverActivity) .setTitle(R.string.fetching) From ff2b24daddac7b6a37c209db28d04cad9ee9ec81 Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:33:25 +0530 Subject: [PATCH 09/10] Create dialog_progress_bar.xml --- .../main/res/layout/dialog_progress_bar.xml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/src/main/res/layout/dialog_progress_bar.xml diff --git a/app/src/main/res/layout/dialog_progress_bar.xml b/app/src/main/res/layout/dialog_progress_bar.xml new file mode 100644 index 000000000..35462e74b --- /dev/null +++ b/app/src/main/res/layout/dialog_progress_bar.xml @@ -0,0 +1,24 @@ + + + + + + + + From 8f38dfc92eea807a782b8c87c85d1d528459f71b Mon Sep 17 00:00:00 2001 From: Ishan09811 <156402647+Ishan09811@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:36:10 +0530 Subject: [PATCH 10/10] add new strings --- app/src/main/res/values/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8b866924..d36d9ee6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,6 +3,11 @@ Search An error has occurred + Warning + Fetching + Downloading + Continue + Close Copied to clipboard Emulator Enabled