diff --git a/app/src/main/java/com/itsaky/androidide/activities/SplashActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/SplashActivity.kt index c729e68512..d5d8dc48ad 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/SplashActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/SplashActivity.kt @@ -23,7 +23,9 @@ import android.os.Build import android.os.Bundle import com.itsaky.androidide.app.configuration.CpuArch import com.itsaky.androidide.app.configuration.IDEBuildConfigProvider +import com.itsaky.androidide.ui.showLowStorageDialog import com.itsaky.androidide.utils.FeatureFlags +import com.itsaky.androidide.utils.hasEnoughStorageAvailable import kotlin.system.exitProcess /** @@ -33,6 +35,11 @@ class SplashActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + if (!hasEnoughStorageAvailable()) { + showLowStorageDialog() + return + } + val isX86 = Build.SUPPORTED_ABIS.firstOrNull() in listOf(CpuArch.X86_64.abi, CpuArch.X86.abi) if (isX86 && (!IDEBuildConfigProvider.getInstance().supportsCpuAbi() || !FeatureFlags.isEmulatorUseEnabled)) { diff --git a/app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt b/app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt new file mode 100644 index 0000000000..b97d6cabf6 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt @@ -0,0 +1,33 @@ +package com.itsaky.androidide.ui + +import android.app.Activity +import android.app.AlertDialog +import android.content.ActivityNotFoundException +import android.content.Intent +import android.provider.Settings +import com.itsaky.androidide.resources.R +import com.itsaky.androidide.utils.getMinimumStorageNeeded + + +fun Activity.showLowStorageDialog() { + val appName = getString(R.string.app_name) + val minSpace = getMinimumStorageNeeded() + + AlertDialog.Builder(this) + .setTitle(R.string.err_insufficient_storage_title) + .setMessage(getString(R.string.err_insufficient_storage_msg, appName, minSpace)) + .setCancelable(false) + .setPositiveButton(getString(R.string.action_close_app, appName)) { _, _ -> + finishAndRemoveTask() + } + .setNegativeButton(R.string.action_free_up_space) { _, _ -> + val intent = Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS) + try { + startActivity(intent) + } catch (_: ActivityNotFoundException) { + startActivity(Intent(Settings.ACTION_SETTINGS)) + } + finishAffinity() + } + .show() +} diff --git a/app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt b/app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt new file mode 100644 index 0000000000..41ef2d67f1 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt @@ -0,0 +1,23 @@ +package com.itsaky.androidide.utils + +import android.os.Environment +import android.os.StatFs + +fun hasEnoughStorageAvailable(): Boolean { + return try { + val dataDir = Environment.getDataDirectory() + val stat = StatFs(dataDir.path) + val availableBytes = stat.availableBlocksLong * stat.blockSizeLong + availableBytes > getMinimumStorageNeeded().gigabytesToBytes() + } catch (_: Exception) { false } +} + +fun getMinimumStorageNeeded(): Long { + val minimumStorageStableGB = 4L + val minimumStorageExperimentalGB = 6L + + if (FeatureFlags.isExperimentsEnabled) { + return minimumStorageExperimentalGB; + } + return minimumStorageStableGB; +} diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt b/app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt index 11b082894e..95c1f7e714 100644 --- a/app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt @@ -11,8 +11,8 @@ import com.itsaky.androidide.events.InstallationEvent import com.itsaky.androidide.models.StorageInfo import com.itsaky.androidide.resources.R import com.itsaky.androidide.utils.Environment -import com.itsaky.androidide.utils.FeatureFlags import com.itsaky.androidide.utils.bytesToGigabytes +import com.itsaky.androidide.utils.getMinimumStorageNeeded import com.itsaky.androidide.utils.gigabytesToBytes import com.itsaky.androidide.utils.withStopWatch import com.itsaky.androidide.viewmodel.InstallationState.InstallationComplete @@ -34,17 +34,6 @@ import kotlinx.coroutines.withContext import org.slf4j.LoggerFactory class InstallationViewModel : ViewModel() { - companion object { - private const val MINIMUM_STORAGE_STABLE_GB = 4L - private const val MINIMUM_STORAGE_EXPERIMENTAL_GB = 6L - } - - private val minimumStorageNeeded: Long - get() = if (FeatureFlags.isExperimentsEnabled) { - MINIMUM_STORAGE_EXPERIMENTAL_GB - } else { - MINIMUM_STORAGE_STABLE_GB - } private val log = LoggerFactory.getLogger(InstallationViewModel::class.java) @@ -148,7 +137,7 @@ class InstallationViewModel : ViewModel() { val stat = StatFs(internalStoragePath) val availableStorageInBytes = stat.availableBlocksLong * stat.blockSizeLong - val requiredStorageInBytes = minimumStorageNeeded.gigabytesToBytes() + val requiredStorageInBytes = getMinimumStorageNeeded().gigabytesToBytes() val isLowStorage = availableStorageInBytes < requiredStorageInBytes diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index ccc30cd1dd..f2aefb5ebb 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -27,6 +27,10 @@ App Logs IDE Logs General information about this feature. + Insufficient Storage + %1$s requires at least %2$dGB of free space to run and extract the necessary tools.\n\nPlease free up space on your device and try again. + Close %1$s + Free Up Space 🤖 System: %1$s backend selected.