diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ede1c015a..8d1fccd84 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
tools:ignore="ScopedStorage" />
+
finishAffinity() }
- builder.create().show()
+ private fun checkDeviceSupported(): Boolean {
+ val configProvider = IDEBuildConfigProvider.getInstance()
+
+ val supported = if (
+ configProvider.isArm64v8aBuild()
+ && !configProvider.isArm64v8aDevice()
+ && configProvider.isArmeabiv7aDevice()
+ ) {
+ // IDE = 64-bit
+ // Device = 32-bit
+ // NOT SUPPORTED
+ DialogUtils.show64bitOn32bit(this)
+ false
+
+ } else if (
+ configProvider.isArmeabiv7aBuild()
+ && configProvider.isArm64v8aDevice()
+ ) {
+ // IDE = 32-bit
+ // Device = 64-bit
+ // SUPPORTED, but warn the user
+ DialogUtils.show32bitOn64bit(this)
+ true
+
+ } else configProvider.supportsBuildFlavor()
+
+ if (!supported) {
+ DialogUtils.showDeviceNotSupported(this)
+ }
+
+
+ return supported
}
private fun openLastProject() {
diff --git a/app/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProviderImpl.kt b/app/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProviderImpl.kt
new file mode 100644
index 000000000..f24c379a9
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProviderImpl.kt
@@ -0,0 +1,69 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.androidide.app
+
+import android.os.Build
+import com.google.auto.service.AutoService
+import com.itsaky.androidide.BuildConfig
+
+/**
+ * @author Akash Yadav
+ */
+@AutoService(IDEBuildConfigProvider::class)
+@Suppress("UNUSED")
+open class IDEBuildConfigProviderImpl : IDEBuildConfigProvider {
+
+ override val buildFlavor: String
+ get() = BuildConfig.FLAVOR
+
+ override val supportedBuildFlavors: Array
+ get() {
+ val flavors = mutableListOf()
+ if (supportedAbis.contains(BuildConfig.FLAVOR_ARM64_V8A)) {
+ flavors.add(BuildConfig.FLAVOR_ARM64_V8A)
+ }
+ if (supportedAbis.contains(BuildConfig.FLAVOR_ARMEABI_V7A)) {
+ flavors.add(BuildConfig.FLAVOR_ARMEABI_V7A)
+ }
+
+ return flavors.toTypedArray()
+ }
+
+ override val supportedAbis: Array
+ get() = Build.SUPPORTED_ABIS
+
+ override fun isArm64v8aBuild(): Boolean {
+ return buildFlavor == BuildConfig.FLAVOR_ARM64_V8A
+ }
+
+ override fun isArm64v8aDevice(): Boolean {
+ return Build.SUPPORTED_64_BIT_ABIS.contains(BuildConfig.FLAVOR_ARM64_V8A)
+ }
+
+ override fun isArmeabiv7aDevice(): Boolean {
+ return Build.SUPPORTED_32_BIT_ABIS.contains(BuildConfig.FLAVOR_ARMEABI_V7A)
+ }
+
+ override fun isArmeabiv7aBuild(): Boolean {
+ return buildFlavor == BuildConfig.FLAVOR_ARMEABI_V7A
+ }
+
+ override fun supportsBuildFlavor(): Boolean {
+ return supportedBuildFlavors.contains(buildFlavor)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt b/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt
index 3e4f0385e..b8dc136c4 100644
--- a/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt
+++ b/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt
@@ -24,7 +24,7 @@ import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputLayout
import com.itsaky.androidide.R
-import com.itsaky.androidide.app.BaseApplication
+import com.itsaky.androidide.app.IDEBuildConfigProvider
import com.itsaky.androidide.preferences.internal.CUSTOM_GRADLE_INSTALLATION
import com.itsaky.androidide.preferences.internal.GRADLE_CLEAR_CACHE
import com.itsaky.androidide.preferences.internal.GRADLE_COMMANDS
@@ -74,7 +74,7 @@ private class GradleOptions(
addPreference(GradleCommands())
addPreference(GradleDistrubution())
addPreference(GradleClearCache())
- if (BaseApplication.isAarch64() && VERSION.SDK_INT == VERSION_CODES.R) {
+ if (IDEBuildConfigProvider.getInstance().isArm64v8aBuild() && VERSION.SDK_INT == VERSION_CODES.R) {
addPreference(TagPointersFix())
}
}
@@ -218,4 +218,5 @@ private class LaunchAppAfterInstall(
override val title: Int = R.string.idepref_launchAppAfterInstall_title,
override val summary: Int? = R.string.idepref_launchAppAfterInstall_summary,
override val icon: Int? = drawable.ic_open_external
-) : SwitchPreference(setValue = ::launchAppAfterInstall::set, getValue = ::launchAppAfterInstall::get)
\ No newline at end of file
+) :
+ SwitchPreference(setValue = ::launchAppAfterInstall::set, getValue = ::launchAppAfterInstall::get)
\ No newline at end of file
diff --git a/app/src/test/java/com/itsaky/androidide/app/IDEBuildConfigProviderImplTest.kt b/app/src/test/java/com/itsaky/androidide/app/IDEBuildConfigProviderImplTest.kt
new file mode 100644
index 000000000..d273c087d
--- /dev/null
+++ b/app/src/test/java/com/itsaky/androidide/app/IDEBuildConfigProviderImplTest.kt
@@ -0,0 +1,111 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.androidide.app
+
+import com.google.common.truth.Truth.assertThat
+import com.itsaky.androidide.BuildConfig
+import org.junit.Test
+
+/**
+ * @author Akash Yadav
+ */
+class IDEBuildConfigProviderImplTest {
+
+ @Test
+ fun `test build config provider`() {
+ object : IDEBuildConfigProviderImpl() {
+ var flavor = fun () = BuildConfig.FLAVOR_ARM64_V8A
+ var abis = fun () = arrayOf(BuildConfig.FLAVOR_ARM64_V8A, BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ override val buildFlavor: String
+ get() = flavor()
+ override val supportedAbis: Array
+ get() = abis()
+ }.apply {
+
+ // ======================= AARCH64 variant on an AARCH64 device ==================
+ assertThat(buildFlavor).isEqualTo(BuildConfig.FLAVOR_ARM64_V8A)
+
+ assertThat(supportedAbis.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARM64_V8A,
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+ assertThat(supportedBuildFlavors.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARM64_V8A,
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+
+ assertThat(isArm64v8aBuild()).isTrue()
+ assertThat(isArmeabiv7aBuild()).isFalse()
+ assertThat(supportsBuildFlavor()).isTrue()
+
+ // ======================= ARM32 variant on an ARM32 device ==================
+ flavor = fun () = BuildConfig.FLAVOR_ARMEABI_V7A
+ abis = fun () = arrayOf(BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ assertThat(buildFlavor).isEqualTo(BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ assertThat(supportedAbis.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+ assertThat(supportedBuildFlavors.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+
+ assertThat(isArm64v8aBuild()).isFalse()
+ assertThat(isArmeabiv7aBuild()).isTrue()
+ assertThat(supportsBuildFlavor()).isTrue()
+
+ // ======================= ARM32 variant on an AARCH64 device ==================
+ flavor = fun () = BuildConfig.FLAVOR_ARMEABI_V7A
+ abis = fun () = arrayOf(BuildConfig.FLAVOR_ARM64_V8A, BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ assertThat(buildFlavor).isEqualTo(BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ assertThat(supportedAbis.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARM64_V8A,
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+ assertThat(supportedBuildFlavors.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARM64_V8A,
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+
+ assertThat(isArm64v8aBuild()).isFalse()
+ assertThat(isArmeabiv7aBuild()).isTrue()
+ assertThat(supportsBuildFlavor()).isTrue()
+
+ // ======================= AARCH64 variant on an ARM32 device ==================
+ flavor = fun () = BuildConfig.FLAVOR_ARM64_V8A
+ abis = fun () = arrayOf(BuildConfig.FLAVOR_ARMEABI_V7A)
+
+ assertThat(buildFlavor).isEqualTo(BuildConfig.FLAVOR_ARM64_V8A)
+
+ assertThat(supportedAbis.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+ assertThat(supportedBuildFlavors.toList()).containsExactly(
+ BuildConfig.FLAVOR_ARMEABI_V7A
+ )
+
+ assertThat(isArm64v8aBuild()).isTrue()
+ assertThat(isArmeabiv7aBuild()).isFalse()
+ assertThat(supportsBuildFlavor()).isFalse()
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/itsaky/androidide/app/BaseApplication.java b/common/src/main/java/com/itsaky/androidide/app/BaseApplication.java
index d818acea3..7a34c79c1 100644
--- a/common/src/main/java/com/itsaky/androidide/app/BaseApplication.java
+++ b/common/src/main/java/com/itsaky/androidide/app/BaseApplication.java
@@ -21,8 +21,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.net.Uri;
-import android.os.Build;
-import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import com.blankj.utilcode.util.ThrowableUtils;
import com.itsaky.androidide.buildinfo.BuildInfo;
@@ -35,8 +33,6 @@ import com.itsaky.androidide.utils.FlashbarUtilsKt;
import com.itsaky.androidide.utils.JavaCharacter;
import com.itsaky.androidide.utils.VMUtils;
import java.io.File;
-import java.util.Arrays;
-import kotlin.collections.ArraysKt;
public class BaseApplication extends Application {
@@ -46,8 +42,6 @@ public class BaseApplication extends Application {
public static final String SPONSOR_URL = BuildInfo.PROJECT_SITE + "/donate";
public static final String DOCS_URL = BuildInfo.PROJECT_SITE + "/docs";
public static final String EMAIL = "contact@androidide.com";
- private static final String AARCH64 = "arm64-v8a";
- private static final String ARM = "armeabi-v7a";
private static BaseApplication instance;
private PreferenceManager mPrefsManager;
@@ -55,28 +49,6 @@ public class BaseApplication extends Application {
return instance;
}
- public static boolean isAbiSupported() {
- return Arrays.asList(Build.SUPPORTED_ABIS).contains(getArch());
- }
-
- public static boolean isAarch64() {
- return ArraysKt.contains(Build.SUPPORTED_64_BIT_ABIS, AARCH64);
- }
-
- public static boolean isArmv7a() {
- return ArraysKt.contains(Build.SUPPORTED_32_BIT_ABIS, ARM);
- }
-
- @Nullable
- public static String getArch() {
- if (isAarch64()) {
- return AARCH64;
- } else if (isArmv7a()) {
- return ARM;
- }
- return null;
- }
-
@Override
public void onCreate() {
instance = this;
@@ -106,10 +78,6 @@ public class BaseApplication extends Application {
ThrowableUtils.getFullStackTrace(th));
}
- public final File getTempProjectDir() {
- return Environment.mkdirIfNotExits(new File(Environment.TMP_DIR, "tempProject"));
- }
-
public PreferenceManager getPrefManager() {
return mPrefsManager;
}
diff --git a/common/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProvider.kt b/common/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProvider.kt
new file mode 100644
index 000000000..161c3d041
--- /dev/null
+++ b/common/src/main/java/com/itsaky/androidide/app/IDEBuildConfigProvider.kt
@@ -0,0 +1,83 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.androidide.app
+
+import com.itsaky.androidide.utils.ServiceLoader
+
+/**
+ * [IDEBuildConfigProvider] provides information about the build variant of the IDE itself.
+ *
+ * @author Akash Yadav
+ */
+interface IDEBuildConfigProvider {
+
+ /**
+ * The current product flavor of the IDE.
+ */
+ val buildFlavor: String
+
+ /**
+ * The build flavors which can be installed on the current device.
+ */
+ val supportedBuildFlavors: Array
+
+ /**
+ * The supported CPU architectures of the device.
+ */
+ val supportedAbis: Array
+
+ /**
+ * @return Whether this build is the 64-bit variant of the IDE.
+ */
+ fun isArm64v8aBuild(): Boolean
+
+ /**
+ * @return Whether the device supports `arm64-v8a` instructions.
+ */
+ fun isArm64v8aDevice() : Boolean
+
+ /**
+ * @return Whether this build is the 32-bit variant of the IDE.
+ */
+ fun isArmeabiv7aBuild(): Boolean
+
+ /**
+ * @return Whether the device supports `armeabi-v7a` instructions.
+ */
+ fun isArmeabiv7aDevice() : Boolean
+
+ /**
+ * @return Whether the IDE can be run on the device's CPU arch.
+ */
+ fun supportsBuildFlavor() : Boolean
+
+ companion object {
+
+ private val _instance by lazy {
+ ServiceLoader.load(IDEBuildConfigProvider::class.java).findFirstOrThrow()
+ }
+
+ /**
+ * Get instance of the [IDEBuildConfigProvider] implementation.
+ */
+ @JvmStatic
+ fun getInstance(): IDEBuildConfigProvider {
+ return _instance
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/itsaky/androidide/managers/ToolsManager.java b/common/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
index 01d95a79a..6e6f9d21e 100755
--- a/common/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
+++ b/common/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
@@ -22,6 +22,7 @@ import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.itsaky.androidide.app.BaseApplication;
+import com.itsaky.androidide.app.IDEBuildConfigProvider;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ILogger;
import java.io.File;
@@ -37,12 +38,14 @@ import org.jetbrains.annotations.Contract;
public class ToolsManager {
private static final ILogger LOG = ILogger.newInstance("ToolsManager");
- public static String ARCH_SPECIFIC_ASSET_DATA_DIR = "data/" + BaseApplication.getArch();
+
+ public static String ARCH_SPECIFIC_ASSET_DATA_DIR =
+ "data/" + IDEBuildConfigProvider.getInstance().getBuildFlavor();
public static String COMMON_ASSET_DATA_DIR = "data/common";
public static void init(@NonNull BaseApplication app, Runnable onFinish) {
- if (BaseApplication.getArch() == null) {
+ if (!IDEBuildConfigProvider.getInstance().supportsBuildFlavor()) {
LOG.error("Device not supported");
return;
}
diff --git a/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt b/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
index 77f1d6e94..8dd111f88 100644
--- a/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
+++ b/common/src/main/java/com/itsaky/androidide/utils/DialogUtils.kt
@@ -16,11 +16,13 @@
*/
package com.itsaky.androidide.utils
+import android.app.Activity
import android.content.Context
import android.content.DialogInterface.OnClickListener
import android.view.LayoutInflater
import android.view.View
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.itsaky.androidide.common.R
import com.itsaky.androidide.common.databinding.LayoutDialogProgressBinding
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.resources.R.style
@@ -34,6 +36,36 @@ import java.util.concurrent.atomic.AtomicInteger
*/
object DialogUtils {
+ @JvmStatic
+ fun showDeviceNotSupported(context: Activity) {
+ val builder = newMaterialDialogBuilder(context)
+ builder.setTitle(R.string.title_device_not_supported)
+ builder.setMessage(R.string.title_device_not_supported)
+ builder.setCancelable(false)
+ builder.setPositiveButton(android.R.string.ok) { _, _ -> context.finishAffinity() }
+ builder.create().show()
+ }
+
+ @JvmStatic
+ fun show64bitOn32bit(context: Activity) {
+ val builder = newMaterialDialogBuilder(context)
+ builder.setTitle(R.string.title_device_not_supported)
+ builder.setMessage(R.string.msg_64bit_on_32bit_device)
+ builder.setCancelable(false)
+ builder.setPositiveButton(android.R.string.ok) { _, _ -> context.finishAffinity() }
+ builder.create().show()
+ }
+
+ @JvmStatic
+ fun show32bitOn64bit(context: Activity) {
+ val builder = newMaterialDialogBuilder(context)
+ builder.setTitle(R.string.title_32bit_on_64bit_device)
+ builder.setMessage(R.string.msg_32bit_on_64bit_device)
+ builder.setCancelable(false)
+ builder.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
+ builder.create().show()
+ }
+
@JvmStatic
@JvmOverloads
fun newProgressDialog(
diff --git a/common/src/main/java/com/itsaky/androidide/utils/Environment.java b/common/src/main/java/com/itsaky/androidide/utils/Environment.java
index 12dcaa5f3..3790f689a 100755
--- a/common/src/main/java/com/itsaky/androidide/utils/Environment.java
+++ b/common/src/main/java/com/itsaky/androidide/utils/Environment.java
@@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.itsaky.androidide.app.BaseApplication;
+import com.itsaky.androidide.app.IDEBuildConfigProvider;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
@@ -218,7 +219,7 @@ public final class Environment {
ENV_VARS.put("TERMUX_PKG_NO_MIRROR_SELECT", "true");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
- && BaseApplication.isAarch64()
+ && IDEBuildConfigProvider.getInstance().isArm64v8aBuild()
&& LIB_HOOK.exists()
&& BaseApplication.getBaseInstance().getPrefManager().shouldUseLdPreload()) {
// Required for JDK 11
diff --git a/idestats/src/main/java/com/itsaky/androidide/stats/AndroidIDEStats.kt b/idestats/src/main/java/com/itsaky/androidide/stats/AndroidIDEStats.kt
index 88fedd137..93b27ec8c 100644
--- a/idestats/src/main/java/com/itsaky/androidide/stats/AndroidIDEStats.kt
+++ b/idestats/src/main/java/com/itsaky/androidide/stats/AndroidIDEStats.kt
@@ -22,6 +22,7 @@ import android.os.Build
import android.telephony.TelephonyManager
import com.blankj.utilcode.util.DeviceUtils
import com.itsaky.androidide.app.BaseApplication
+import com.itsaky.androidide.app.IDEBuildConfigProvider
import com.itsaky.androidide.buildinfo.BuildInfo
import java.math.BigInteger
import java.security.MessageDigest
@@ -58,7 +59,7 @@ object AndroidIDEStats {
}
val cpuArch by lazy {
- BaseApplication.getArch()!!
+ IDEBuildConfigProvider.getInstance().buildFlavor
}
val statData by lazy {
diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml
index 7027734bb..02977093b 100644
--- a/resources/src/main/res/values/strings.xml
+++ b/resources/src/main/res/values/strings.xml
@@ -53,6 +53,7 @@
Device Not Supported
Unfortunately, your device is not supported at the moment. We are sorry for the inconvenience.\n\nSupported CPU ABIs: aarch64 and armeabi-v7a
+
Build
Build output
Lint tasks
@@ -610,4 +611,7 @@
Cannot run app. Unable to determine package name.
Cannot run application. No application modules found in project.
Launch app
+ You are using 64-bit variant of AndroidIDE on a 32-bit-only device. This configuration is not supported. Please install the 32-bit variant of AndroidIDE.
+ Configuration Warning
+ You are using 32-bit variant of AndroidIDE on a 64-bit device. This configuration may work, but it\'ll result in reduced performance and possible security vulnerabilities. Please avoid using this configuration whenever possible.