Skip to content

Commit c7b15c4

Browse files
committed
Bump the minimum supported SDK version to 24
Raise the minimum supported Android version from Android 5 (Lollipop) to Android 7 (Nougat).
1 parent 6a6a116 commit c7b15c4

File tree

15 files changed

+89
-179
lines changed

15 files changed

+89
-179
lines changed

platform/android/detect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def get_ndk_version():
7373

7474
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
7575
def get_min_target_api():
76-
return 21
76+
return 24
7777

7878

7979
def get_flags():

platform/android/export/export_plugin.cpp

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,7 @@ static const int EXPORT_FORMAT_AAB = 1;
281281
static const char *APK_ASSETS_DIRECTORY = "assets";
282282
static const char *AAB_ASSETS_DIRECTORY = "assetPacks/installTime/src/main/assets";
283283

284-
static const int OPENGL_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
285-
static const int VULKAN_MIN_SDK_VERSION = 24;
284+
static const int DEFAULT_MIN_SDK_VERSION = 24; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
286285
static const int DEFAULT_TARGET_SDK_VERSION = 34; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
287286

288287
#ifndef ANDROID_ENABLED
@@ -1963,7 +1962,6 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
19631962
}
19641963
} else if (p_name == "gradle_build/min_sdk") {
19651964
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
1966-
int min_sdk_int = VULKAN_MIN_SDK_VERSION;
19671965
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
19681966
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
19691967
if (!gradle_build_enabled) {
@@ -1972,9 +1970,9 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
19721970
if (!min_sdk_str.is_valid_int()) {
19731971
return vformat(TTR("\"Min SDK\" should be a valid integer, but got \"%s\" which is invalid."), min_sdk_str);
19741972
} else {
1975-
min_sdk_int = min_sdk_str.to_int();
1976-
if (min_sdk_int < OPENGL_MIN_SDK_VERSION) {
1977-
return vformat(TTR("\"Min SDK\" cannot be lower than %d, which is the version needed by the Godot library."), OPENGL_MIN_SDK_VERSION);
1973+
int min_sdk_int = min_sdk_str.to_int();
1974+
if (min_sdk_int < DEFAULT_MIN_SDK_VERSION) {
1975+
return vformat(TTR("\"Min SDK\" cannot be lower than %d, which is the version needed by the Godot library."), DEFAULT_MIN_SDK_VERSION);
19781976
}
19791977
}
19801978
}
@@ -1983,7 +1981,7 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
19831981
int target_sdk_int = DEFAULT_TARGET_SDK_VERSION;
19841982

19851983
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
1986-
int min_sdk_int = VULKAN_MIN_SDK_VERSION;
1984+
int min_sdk_int = DEFAULT_MIN_SDK_VERSION;
19871985
if (min_sdk_str.is_valid_int()) {
19881986
min_sdk_int = min_sdk_str.to_int();
19891987
}
@@ -2032,7 +2030,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
20322030
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK, false, true));
20332031
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
20342032
// This implies doing validation that the string is a proper int.
2035-
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", VULKAN_MIN_SDK_VERSION)), "", false, true));
2033+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "", false, true));
20362034
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "", false, true));
20372035

20382036
#ifndef DISABLE_DEPRECATED
@@ -2903,14 +2901,6 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
29032901
}
29042902
}
29052903

2906-
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
2907-
int min_sdk_int = VULKAN_MIN_SDK_VERSION;
2908-
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
2909-
if (min_sdk_str.is_valid_int()) {
2910-
min_sdk_int = min_sdk_str.to_int();
2911-
}
2912-
}
2913-
29142904
String target_sdk_str = p_preset->get("gradle_build/target_sdk");
29152905
int target_sdk_int = DEFAULT_TARGET_SDK_VERSION;
29162906
if (!target_sdk_str.is_empty()) { // Empty means no override, nothing to do.
@@ -2931,12 +2921,6 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
29312921
err += "\n";
29322922
}
29332923

2934-
if (_uses_vulkan(p_preset) && min_sdk_int < VULKAN_MIN_SDK_VERSION) {
2935-
// Warning only, so don't override `valid`.
2936-
err += vformat(TTR("\"Min SDK\" should be greater or equal to %d for the \"%s\" renderer."), VULKAN_MIN_SDK_VERSION, current_renderer);
2937-
err += "\n";
2938-
}
2939-
29402924
String package_name = p_preset->get("package/unique_name");
29412925
if (package_name.contains("$genname") && !is_project_name_valid(p_preset)) {
29422926
// Warning only, so don't override `valid`.
@@ -3507,7 +3491,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
35073491
String version_name = p_preset->get_version("version/name");
35083492
String min_sdk_version = p_preset->get("gradle_build/min_sdk");
35093493
if (!min_sdk_version.is_valid_int()) {
3510-
min_sdk_version = itos(VULKAN_MIN_SDK_VERSION);
3494+
min_sdk_version = itos(DEFAULT_MIN_SDK_VERSION);
35113495
}
35123496
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
35133497
if (!target_sdk_version.is_valid_int()) {

platform/android/java/app/config.gradle

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
ext.versions = [
22
androidGradlePlugin: '8.2.0',
33
compileSdk : 34,
4-
// Also update 'platform/android/export/export_plugin.cpp#OPENGL_MIN_SDK_VERSION'
5-
minSdk : 21,
4+
// Also update:
5+
// - 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
6+
// - 'platform/android/detect.py#get_min_target_api()'
7+
minSdk : 24,
68
// Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
79
targetSdk : 34,
810
buildTools : '34.0.0',
@@ -382,17 +384,12 @@ ext.shouldNotStrip = { ->
382384
*/
383385
ext.shouldUseLegacyPackaging = { ->
384386
int minSdk = getExportMinSdkVersion()
385-
if (minSdk < 23) {
386-
// Enforce the default behavior for compatibility with device running api < 23
387-
return true
388-
}
389-
390387
String legacyPackagingFlag = project.hasProperty("compress_native_libraries") ? project.property("compress_native_libraries") : ""
391388
if (legacyPackagingFlag != null && !legacyPackagingFlag.isEmpty()) {
392389
return Boolean.parseBoolean(legacyPackagingFlag)
393390
}
394391

395-
// Default behavior for minSdk >= 23
392+
// Default behavior for minSdk >= 24
396393
return false
397394
}
398395

platform/android/java/editor/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ android {
162162
}
163163
applicationIdSuffix ".meta"
164164
versionNameSuffix "-meta"
165-
minSdkVersion 23
166165
targetSdkVersion 32
167166
}
168167
picoos {

platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,11 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
192192
Manifest.permission.RECORD_AUDIO,
193193
)
194194

195-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
196-
excludedPermissions.add(
197-
// The REQUEST_INSTALL_PACKAGES permission is requested the first time we attempt to
198-
// open an apk file.
199-
Manifest.permission.REQUEST_INSTALL_PACKAGES,
200-
)
201-
}
195+
excludedPermissions.add(
196+
// The REQUEST_INSTALL_PACKAGES permission is requested the first time we attempt to
197+
// open an apk file.
198+
Manifest.permission.REQUEST_INSTALL_PACKAGES,
199+
)
202200

203201
// XR runtime permissions should only be requested when the "xr/openxr/enabled" project setting
204202
// is enabled.
@@ -384,10 +382,8 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
384382

385383
val launchPolicy = resolveLaunchPolicyIfNeeded(editorWindowInfo.launchPolicy)
386384
if (launchPolicy == LaunchPolicy.ADJACENT) {
387-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
388-
Log.v(TAG, "Adding flag for adjacent launch")
389-
newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)
390-
}
385+
Log.v(TAG, "Adding flag for adjacent launch")
386+
newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)
391387
}
392388
return newInstance
393389
}
@@ -511,12 +507,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
511507
private fun resolveGameEmbedModeIfNeeded(embedMode: GameEmbedMode): GameEmbedMode {
512508
return when (embedMode) {
513509
GameEmbedMode.AUTO -> {
514-
val inMultiWindowMode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
515-
isInMultiWindowMode
516-
} else {
517-
false
518-
}
519-
if (inMultiWindowMode || isLargeScreen || isNativeXRDevice(applicationContext)) {
510+
if (isInMultiWindowMode || isLargeScreen || isNativeXRDevice(applicationContext)) {
520511
GameEmbedMode.DISABLED
521512
} else {
522513
GameEmbedMode.ENABLED
@@ -534,12 +525,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
534525
private fun resolveLaunchPolicyIfNeeded(policy: LaunchPolicy): LaunchPolicy {
535526
return when (policy) {
536527
LaunchPolicy.AUTO -> {
537-
val inMultiWindowMode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
538-
isInMultiWindowMode
539-
} else {
540-
false
541-
}
542-
val defaultLaunchPolicy = if (inMultiWindowMode || isLargeScreen || isNativeXRDevice(applicationContext)) {
528+
val defaultLaunchPolicy = if (isInMultiWindowMode || isLargeScreen || isNativeXRDevice(applicationContext)) {
543529
LaunchPolicy.ADJACENT
544530
} else {
545531
LaunchPolicy.SAME

platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ open class GodotGame : BaseGodotGame() {
8383
}
8484

8585
override fun enterPiPMode() {
86-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && hasPiPSystemFeature()) {
86+
if (hasPiPSystemFeature()) {
8787
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
8888
val builder = PictureInPictureParams.Builder().setSourceRectHint(gameViewSourceRectHint)
8989
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -101,8 +101,7 @@ open class GodotGame : BaseGodotGame() {
101101
* Returns true the if the device supports picture-in-picture (PiP).
102102
*/
103103
protected fun hasPiPSystemFeature(): Boolean {
104-
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
105-
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
104+
return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
106105
}
107106

108107
override fun shouldShowGameMenuBar(): Boolean {
@@ -123,8 +122,7 @@ open class GodotGame : BaseGodotGame() {
123122
override fun onStop() {
124123
super.onStop()
125124

126-
val isInPiPMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInPictureInPictureMode
127-
if (isInPiPMode && !isFinishing) {
125+
if (isInPictureInPictureMode && !isFinishing) {
128126
// We get in this state when PiP is closed, so we terminate the activity.
129127
finish()
130128
}

platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,19 +225,12 @@ static public String generateSaveFileName(Context c, String fileName) {
225225
return path;
226226
}
227227

228-
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
229228
static public String getSaveFilePath(Context c) {
230229
// This technically existed since Honeycomb, but it is critical
231230
// on KitKat and greater versions since it will create the
232231
// directory if needed
233-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
234-
return c.getObbDir().toString();
235-
} else {
236-
File root = Environment.getExternalStorageDirectory();
237-
String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
238-
return path;
239-
}
240-
}
232+
return c.getObbDir().toString();
233+
}
241234

242235
/**
243236
* Helper function to ascertain the existence of a file and return true/false appropriately
@@ -297,7 +290,7 @@ static public int getFileStatus(Context c, String fileName) {
297290
/**
298291
* Helper function to ascertain whether the application has the correct access to the OBB
299292
* directory to allow an OBB file to be written.
300-
*
293+
*
301294
* @param c the app/activity/service context
302295
* @return true if the application can write an OBB file, false otherwise
303296
*/
@@ -317,7 +310,7 @@ static public boolean canWriteOBBFile(Context c) {
317310
* Converts download states that are returned by the
318311
* {@link IDownloaderClient#onDownloadStateChanged} callback into usable strings. This is useful
319312
* if using the state strings built into the library to display user messages.
320-
*
313+
*
321314
* @param state One of the STATE_* constants from {@link IDownloaderClient}.
322315
* @return string resource ID for the corresponding string.
323316
*/

platform/android/java/lib/src/org/godotengine/godot/Godot.kt

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -865,16 +865,13 @@ class Godot(private val context: Context) {
865865
if (packageManager == null) {
866866
return false
867867
}
868-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
869-
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) {
870-
// Optional requirements.. log as warning if missing
871-
Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1")
872-
}
873-
874-
// Check for api version 1.0
875-
return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003)
868+
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) {
869+
// Optional requirements.. log as warning if missing
870+
Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1")
876871
}
877-
return false
872+
873+
// Check for api version 1.0
874+
return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003)
878875
}
879876

880877
private fun setKeepScreenOn(enabled: Boolean) {

platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,8 @@ private void performEngineInitialization() {
221221
Intent notifierIntent = new Intent(activity, activity.getClass());
222222
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
223223

224-
PendingIntent pendingIntent;
225-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
226-
pendingIntent = PendingIntent.getActivity(activity, 0,
227-
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
228-
} else {
229-
pendingIntent = PendingIntent.getActivity(activity, 0,
230-
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
231-
}
224+
PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0,
225+
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
232226

233227
int startResult;
234228
try {

platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHan
9090
this.godot = godot;
9191
this.inputHandler = inputHandler;
9292
this.godotRenderer = new GodotRenderer();
93-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
94-
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
95-
}
93+
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
9694
init(xrMode, false, useDebugOpengl);
9795
}
9896

@@ -199,27 +197,25 @@ public void releasePointerCapture() {
199197
@Keep
200198
@Override
201199
public void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY) {
202-
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
203-
try {
204-
Bitmap bitmap = null;
205-
if (!TextUtils.isEmpty(imagePath)) {
206-
if (godot.getDirectoryAccessHandler().filesystemFileExists(imagePath)) {
207-
// Try to load the bitmap from the file system
208-
bitmap = BitmapFactory.decodeFile(imagePath);
209-
} else if (godot.getDirectoryAccessHandler().assetsFileExists(imagePath)) {
210-
// Try to load the bitmap from the assets directory
211-
AssetManager am = getContext().getAssets();
212-
InputStream imageInputStream = am.open(imagePath);
213-
bitmap = BitmapFactory.decodeStream(imageInputStream);
214-
}
200+
try {
201+
Bitmap bitmap = null;
202+
if (!TextUtils.isEmpty(imagePath)) {
203+
if (godot.getDirectoryAccessHandler().filesystemFileExists(imagePath)) {
204+
// Try to load the bitmap from the file system
205+
bitmap = BitmapFactory.decodeFile(imagePath);
206+
} else if (godot.getDirectoryAccessHandler().assetsFileExists(imagePath)) {
207+
// Try to load the bitmap from the assets directory
208+
AssetManager am = getContext().getAssets();
209+
InputStream imageInputStream = am.open(imagePath);
210+
bitmap = BitmapFactory.decodeStream(imageInputStream);
215211
}
216-
217-
PointerIcon customPointerIcon = PointerIcon.create(bitmap, hotSpotX, hotSpotY);
218-
customPointerIcons.put(pointerType, customPointerIcon);
219-
} catch (Exception e) {
220-
// Reset the custom pointer icon
221-
customPointerIcons.delete(pointerType);
222212
}
213+
214+
PointerIcon customPointerIcon = PointerIcon.create(bitmap, hotSpotX, hotSpotY);
215+
customPointerIcons.put(pointerType, customPointerIcon);
216+
} catch (Exception e) {
217+
// Reset the custom pointer icon
218+
customPointerIcons.delete(pointerType);
223219
}
224220
}
225221

@@ -229,21 +225,16 @@ public void configurePointerIcon(int pointerType, String imagePath, float hotSpo
229225
@Keep
230226
@Override
231227
public void setPointerIcon(int pointerType) {
232-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
233-
PointerIcon pointerIcon = customPointerIcons.get(pointerType);
234-
if (pointerIcon == null) {
235-
pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
236-
}
237-
setPointerIcon(pointerIcon);
228+
PointerIcon pointerIcon = customPointerIcons.get(pointerType);
229+
if (pointerIcon == null) {
230+
pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
238231
}
232+
setPointerIcon(pointerIcon);
239233
}
240234

241235
@Override
242236
public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
243-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
244-
return getPointerIcon();
245-
}
246-
return super.onResolvePointerIcon(me, pointerIndex);
237+
return getPointerIcon();
247238
}
248239

249240
private void init(XRMode xrMode, boolean translucent, boolean useDebugOpengl) {

0 commit comments

Comments
 (0)