diff options
Diffstat (limited to 'platform/android/java')
34 files changed, 463 insertions, 707 deletions
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 948fa8c00b..00e01884cf 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -19,8 +19,11 @@      <application          android:label="@string/godot_project_name_string"          android:allowBackup="false" -        tools:ignore="GoogleAppIndexingWarning" -        android:icon="@mipmap/icon" > +        android:icon="@mipmap/icon" +        android:isGame="true" +        android:hasFragileUserData="false" +        android:requestLegacyExternalStorage="false" +        tools:ignore="GoogleAppIndexingWarning" >          <!-- Records the version of the Godot editor used for building -->          <meta-data @@ -30,11 +33,6 @@          <!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->          <!-- Do these changes in the export preset. Adding new ones is fine. --> -        <!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. --> -        <meta-data -            android:name="xr_mode_metadata_name" -            android:value="xr_mode_metadata_value" /> -          <activity              android:name=".GodotApp"              android:label="@string/godot_project_name_string" @@ -45,8 +43,8 @@              android:resizeableActivity="false"              tools:ignore="UnusedAttribute" > -            <!-- Focus awareness metadata is updated at export time if the user enables it in the 'Xr Features' section. --> -            <meta-data android:name="com.oculus.vr.focusaware" android:value="false" /> +            <!-- Focus awareness metadata is removed at export time if the xr mode is not VR. --> +            <meta-data android:name="com.oculus.vr.focusaware" android:value="true" />              <intent-filter>                  <action android:name="android.intent.action.MAIN" /> diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index f103f22db2..18e07c3762 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -6,7 +6,7 @@ buildscript {      repositories {          google() -        jcenter() +        mavenCentral()      }      dependencies {          classpath libraries.androidGradlePlugin @@ -18,9 +18,8 @@ apply plugin: 'com.android.application'  allprojects {      repositories { -        mavenCentral()          google() -        jcenter() +        mavenCentral()          // Godot user plugins custom maven repos          String[] mavenRepos = getGodotPluginsMavenRepos() @@ -113,6 +112,15 @@ android {      }      signingConfigs { +        debug { +            if (hasCustomDebugKeystore()) { +                storeFile new File(getDebugKeystoreFile()) +                storePassword getDebugKeystorePassword() +                keyAlias getDebugKeyAlias() +                keyPassword getDebugKeystorePassword() +            } +        } +          release {              File keystoreFile = new File(getReleaseKeystoreFile())              if (keystoreFile.isFile()) { @@ -157,7 +165,7 @@ android {              aidl.srcDirs = ['aidl']              assets.srcDirs = ['assets']          } -        debug.jniLibs.srcDirs = ['libs/debug'] +        debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']          release.jniLibs.srcDirs = ['libs/release']      } diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index c0ae4007d2..fad64c675f 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,11 +1,11 @@  ext.versions = [ -    androidGradlePlugin: '4.0.1', -    compileSdk         : 29, -    minSdk             : 18, -    targetSdk          : 29, -    buildTools         : '30.0.1', +    androidGradlePlugin: '4.2.2', +    compileSdk         : 30, +    minSdk             : 19, +    targetSdk          : 30, +    buildTools         : '30.0.3',      supportCoreUtils   : '1.0.0', -    kotlinVersion      : '1.4.10', +    kotlinVersion      : '1.5.10',      v4Support          : '1.0.0',      javaVersion        : 1.8,      ndkVersion         : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated. @@ -191,6 +191,35 @@ ext.getGodotPluginsLocalBinaries = { ->      return binDeps  } +ext.getDebugKeystoreFile = { -> +    String keystoreFile = project.hasProperty("debug_keystore_file") ? project.property("debug_keystore_file") : "" +    if (keystoreFile == null || keystoreFile.isEmpty()) { +        keystoreFile = "." +    } +    return keystoreFile +} + +ext.hasCustomDebugKeystore = { -> +    File keystoreFile = new File(getDebugKeystoreFile()) +    return keystoreFile.isFile() +} + +ext.getDebugKeystorePassword = { -> +    String keystorePassword = project.hasProperty("debug_keystore_password") ? project.property("debug_keystore_password") : "" +    if (keystorePassword == null || keystorePassword.isEmpty()) { +        keystorePassword = "android" +    } +    return keystorePassword +} + +ext.getDebugKeyAlias = { -> +    String keyAlias = project.hasProperty("debug_keystore_alias") ? project.property("debug_keystore_alias") : "" +    if (keyAlias == null || keyAlias.isEmpty()) { +        keyAlias = "androiddebugkey" +    } +    return keyAlias +} +  ext.getReleaseKeystoreFile = { ->      String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""      if (keystoreFile == null || keystoreFile.isEmpty()) { @@ -239,5 +268,5 @@ ext.shouldSign = { ->  }  ext.shouldNotStrip = { -> -    return isAndroidStudio() +    return isAndroidStudio() || project.hasProperty("doNotStrip")  } diff --git a/platform/android/java/app/gradle.properties b/platform/android/java/app/gradle.properties new file mode 100644 index 0000000000..0ad8e611ca --- /dev/null +++ b/platform/android/java/app/gradle.properties @@ -0,0 +1,25 @@ +# Godot custom build Gradle settings. +# These properties apply when running custom build from the Godot editor. +# NOTE: This should be kept in sync with 'godot/platform/android/java/gradle.properties' except +# where otherwise specified. + +# For more details on how to configure your build environment visit +# https://www.gradle.org/docs/current/userguide/build_environment.html + +android.enableJetifier=true +android.useAndroidX=true + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# https://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +org.gradle.warning.mode=all + +# Enable resource optimizations for release build. +# NOTE: This is turned off for template release build in order to support the build legacy process. +android.enableResourceOptimizations=true diff --git a/platform/android/java/app/res/drawable/splash.png b/platform/android/java/app/res/drawable-nodpi/splash.png Binary files differindex 7bddd4325a..7bddd4325a 100644 --- a/platform/android/java/app/res/drawable/splash.png +++ b/platform/android/java/app/res/drawable-nodpi/splash.png diff --git a/platform/android/java/app/res/drawable/splash_bg_color.png b/platform/android/java/app/res/drawable-nodpi/splash_bg_color.png Binary files differindex 004b6fd508..004b6fd508 100644 --- a/platform/android/java/app/res/drawable/splash_bg_color.png +++ b/platform/android/java/app/res/drawable-nodpi/splash_bg_color.png diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index ec02b0fc7a..87bb2ea218 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -5,7 +5,7 @@ buildscript {      repositories {          google() -        jcenter() +        mavenCentral()      }      dependencies {          classpath libraries.androidGradlePlugin @@ -16,7 +16,6 @@ buildscript {  allprojects {      repositories {          google() -        jcenter()          mavenCentral()      }  } @@ -112,26 +111,27 @@ task copyReleaseAARToBin(type: Copy) {   * The zip file also includes some gradle tools to allow building of the custom build.   */  task zipCustomBuild(type: Zip) { -    dependsOn ':generateGodotTemplates' +    onlyIf { generateGodotTemplates.state.executed || generateDevTemplate.state.executed }      doFirst {          logger.lifecycle("Generating Godot custom build template")      } -    from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**'])) +    from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))      include '**/*' -    archiveName 'android_source.zip' -    destinationDir(file(binDir)) +    archiveFileName = 'android_source.zip' +    destinationDirectory = file(binDir)  } -/** - * Master task used to coordinate the tasks defined above to generate the set of Godot templates. - */ -task generateGodotTemplates(type: GradleBuild) { +def templateExcludedBuildTask() {      // We exclude these gradle tasks so we can run the scons command manually. +    def excludedTasks = []      for (String buildType : supportedTargets) { -        startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType) +        excludedTasks += ":lib:" + getSconsTaskName(buildType)      } +    return excludedTasks +} -    tasks = [] +def templateBuildTasks() { +    def tasks = []      // Only build the apks and aar files for which we have native shared libraries.      for (String target : supportedTargets) { @@ -152,6 +152,29 @@ task generateGodotTemplates(type: GradleBuild) {          }      } +    return tasks +} + +/** + * Master task used to coordinate the tasks defined above to generate the set of Godot templates. + */ +task generateGodotTemplates(type: GradleBuild) { +    startParameter.excludedTaskNames = templateExcludedBuildTask() +    tasks = templateBuildTasks() + +    finalizedBy 'zipCustomBuild' +} + +/** + * Generates the same output as generateGodotTemplates but with dev symbols + */ +task generateDevTemplate (type: GradleBuild) { +    // add parameter to set symbols to true +    startParameter.projectProperties += [doNotStrip: true] + +    startParameter.excludedTaskNames = templateExcludedBuildTask() +    tasks = templateBuildTasks() +      finalizedBy 'zipCustomBuild'  } diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties index 2dc069ad2f..5cd94e85d9 100644 --- a/platform/android/java/gradle.properties +++ b/platform/android/java/gradle.properties @@ -1,22 +1,28 @@  # Project-wide Gradle settings. +# NOTE: This should be kept in sync with 'godot/platform/android/java/app/gradle.properties' except +# where otherwise specified.  # IDE (e.g. Android Studio) users:  # Gradle settings configured through the IDE *will override*  # any settings specified in this file.  # For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html +# https://www.gradle.org/docs/current/userguide/build_environment.html  android.enableJetifier=true  android.useAndroidX=true  # Specifies the JVM arguments used for the daemon process.  # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m +org.gradle.jvmargs=-Xmx4536m  # When configured, Gradle will run in incubating parallel mode.  # This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# https://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects  # org.gradle.parallel=true  org.gradle.warning.mode=all + +# Disable resource optimizations for template release build. +# NOTE: This is turned on for custom build in order to improve the release build. +android.enableResourceOptimizations=false diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index a7d8a0f310..74c5636f8a 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Sep 02 02:44:30 PDT 2019 +#Wed Jun 23 23:42:22 PDT 2021  distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip  distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME  zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat index f9553162f1..11cc30edb0 100644 --- a/platform/android/java/gradlew.bat +++ b/platform/android/java/gradlew.bat @@ -1,7 +1,7 @@  @if "%DEBUG%" == "" @echo off  @rem ##########################################################################  @rem -@rem  Gradle startup script for Windows +@rem Gradle startup script for Windows  @rem  @rem ########################################################################## @@ -75,7 +75,7 @@ if "%ERRORLEVEL%"=="0" goto mainEnd  :fail  rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of  rem the _cmd.exe /c_ return code! -if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1  exit /b 1  :mainEnd diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml index 590b066d8a..010006b81e 100644 --- a/platform/android/java/lib/res/values/strings.xml +++ b/platform/android/java/lib/res/values/strings.xml @@ -6,7 +6,7 @@      <string name="text_button_resume_cellular">Resume download</string>      <string name="text_button_wifi_settings">Wi-Fi settings</string>      <string name="text_verifying_download">Verifying Download</string> -    <string name="text_validation_complete">XAPK File Validation Complete.  Select OK to exit.</string> +    <string name="text_validation_complete">XAPK File Validation Complete. Select OK to exit.</string>      <string name="text_validation_failed">XAPK File Validation Failed.</string>      <string name="text_button_pause">Pause Download</string>      <string name="text_button_resume">Resume Download</string> diff --git a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java index 2a72c9818d..9aa65fd786 100644 --- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java +++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java @@ -54,7 +54,7 @@ public class Helpers {      /*       * Parse the Content-Disposition HTTP Header. The format of the header is defined here: -     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for +     * https://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for       * content that is going to be downloaded to the file system. We only support the attachment       * type.       */ diff --git a/platform/android/java/lib/src/com/google/android/vending/licensing/Obfuscator.java b/platform/android/java/lib/src/com/google/android/vending/licensing/Obfuscator.java index 008c150a8e..05b452d0c1 100644 --- a/platform/android/java/lib/src/com/google/android/vending/licensing/Obfuscator.java +++ b/platform/android/java/lib/src/com/google/android/vending/licensing/Obfuscator.java @@ -20,7 +20,7 @@ package com.google.android.vending.licensing;   * Interface used as part of a {@link Policy} to allow application authors to obfuscate   * licensing data that will be stored into a SharedPreferences file.   * <p> - * Any transformation scheme must be reversable. Implementing classes may optionally implement an + * Any transformation scheme must be reversible. Implementing classes may optionally implement an   * integrity check to further prevent modification to preference data. Implementing classes   * should use device-specific information as a key in the obfuscation algorithm to prevent   * obfuscated preferences from being shared among devices. diff --git a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java index 0572cf3589..b12844702a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java @@ -43,10 +43,10 @@ public class Dictionary extends HashMap<String, Object> {  		for (String key : keys) {  			ret[i] = key;  			i++; -		}; +		}  		return ret; -	}; +	}  	public Object[] get_values() {  		Object[] ret = new Object[size()]; @@ -55,21 +55,21 @@ public class Dictionary extends HashMap<String, Object> {  		for (String key : keys) {  			ret[i] = get(key);  			i++; -		}; +		}  		return ret; -	}; +	}  	public void set_keys(String[] keys) {  		keys_cache = keys; -	}; +	}  	public void set_values(Object[] vals) {  		int i = 0;  		for (String key : keys_cache) {  			put(key, vals[i]);  			i++; -		}; +		}  		keys_cache = null; -	}; -}; +	} +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index ec2ace4821..3600706c7c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -30,13 +30,15 @@  package org.godotengine.godot; +import android.content.ComponentName;  import android.content.Intent;  import android.os.Bundle; -import android.view.KeyEvent; +import android.util.Log;  import androidx.annotation.CallSuper;  import androidx.annotation.NonNull;  import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment;  import androidx.fragment.app.FragmentActivity;  /** @@ -46,6 +48,8 @@ import androidx.fragment.app.FragmentActivity;   * within an Android app.   */  public abstract class FullScreenGodotApp extends FragmentActivity implements GodotHost { +	private static final String TAG = FullScreenGodotApp.class.getSimpleName(); +  	@Nullable  	private Godot godotFragment; @@ -53,40 +57,78 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God  	public void onCreate(Bundle savedInstanceState) {  		super.onCreate(savedInstanceState);  		setContentView(R.layout.godot_app_layout); -		godotFragment = initGodotInstance(); -		if (godotFragment == null) { -			throw new IllegalStateException("Godot instance must be non-null."); + +		Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.godot_fragment_container); +		if (currentFragment instanceof Godot) { +			Log.v(TAG, "Reusing existing Godot fragment instance."); +			godotFragment = (Godot)currentFragment; +		} else { +			Log.v(TAG, "Creating new Godot fragment instance."); +			godotFragment = initGodotInstance(); +			if (godotFragment == null) { +				throw new IllegalStateException("Godot instance must be non-null."); +			} + +			getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();  		} +	} -		getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss(); +	@Override +	public void onDestroy() { +		super.onDestroy(); +		onGodotForceQuit(godotFragment); +	} + +	@Override +	public final void onGodotForceQuit(Godot instance) { +		if (instance == godotFragment) { +			System.exit(0); +		} +	} + +	@Override +	public final void onGodotRestartRequested(Godot instance) { +		if (instance == godotFragment) { +			// HACK: +			// +			// Currently it's very hard to properly deinitialize Godot on Android to restart the game +			// from scratch. Therefore, we need to kill the whole app process and relaunch it. +			// +			// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including +			// releasing and reloading native libs or resetting their state somehow and clearing statics). +			// +			// Using instrumentation is a way of making the whole app process restart, because Android +			// will kill any process of the same package which was already running. +			// +			Bundle args = new Bundle(); +			args.putParcelable("intent", getIntent()); +			startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args); +		}  	}  	@Override  	public void onNewIntent(Intent intent) { +		super.onNewIntent(intent);  		if (godotFragment != null) {  			godotFragment.onNewIntent(intent); -		} else { -			super.onNewIntent(intent);  		}  	}  	@CallSuper  	@Override  	public void onActivityResult(int requestCode, int resultCode, Intent data) { +		super.onActivityResult(requestCode, resultCode, data);  		if (godotFragment != null) {  			godotFragment.onActivityResult(requestCode, resultCode, data); -		} else { -			super.onActivityResult(requestCode, resultCode, data);  		}  	}  	@CallSuper  	@Override  	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { +		super.onRequestPermissionsResult(requestCode, permissions, grantResults);  		if (godotFragment != null) {  			godotFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); -		} else { -			super.onRequestPermissionsResult(requestCode, permissions, grantResults);  		}  	} @@ -99,14 +141,6 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God  		}  	} -	@Override -	public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { -		if (godotFragment != null && godotFragment.onKeyMultiple(inKeyCode, repeatCount, event)) { -			return true; -		} -		return super.onKeyMultiple(inKeyCode, repeatCount, event); -	} -  	/**  	 * Used to initialize the Godot fragment instance in {@link FullScreenGodotApp#onCreate(Bundle)}.  	 */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 0c16214c8a..896b169953 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -49,7 +49,6 @@ import android.content.ClipData;  import android.content.ClipboardManager;  import android.content.ComponentName;  import android.content.Context; -import android.content.DialogInterface;  import android.content.Intent;  import android.content.SharedPreferences;  import android.content.SharedPreferences.Editor; @@ -68,17 +67,12 @@ import android.os.Environment;  import android.os.Messenger;  import android.os.VibrationEffect;  import android.os.Vibrator; -import android.provider.Settings.Secure;  import android.view.Display; -import android.view.InputDevice; -import android.view.KeyEvent;  import android.view.LayoutInflater; -import android.view.MotionEvent;  import android.view.Surface;  import android.view.View;  import android.view.ViewGroup;  import android.view.ViewGroup.LayoutParams; -import android.view.ViewTreeObserver;  import android.view.Window;  import android.view.WindowManager;  import android.widget.Button; @@ -175,7 +169,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	public static GodotNetUtils netUtils;  	public interface ResultCallback { -		public void callback(int requestCode, int resultCode, Intent data); +		void callback(int requestCode, int resultCode, Intent data);  	}  	public ResultCallback result_callback; @@ -220,7 +214,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		for (int i = 0; i < permissions.length; i++) {  			GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);  		} -	}; +	}  	/**  	 * Invoked on the render thread when the Godot setup is complete. @@ -281,29 +275,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		editText.setView(mRenderView);  		io.setEdit(editText); -		view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { -			@Override -			public void onGlobalLayout() { -				Point fullSize = new Point(); -				activity.getWindowManager().getDefaultDisplay().getSize(fullSize); -				Rect gameSize = new Rect(); -				mRenderView.getView().getWindowVisibleDisplayFrame(gameSize); - -				final int keyboardHeight = fullSize.y - gameSize.bottom; -				GodotLib.setVirtualKeyboardHeight(keyboardHeight); -			} +		view.getViewTreeObserver().addOnGlobalLayoutListener(() -> { +			Point fullSize = new Point(); +			activity.getWindowManager().getDefaultDisplay().getSize(fullSize); +			Rect gameSize = new Rect(); +			mRenderView.getView().getWindowVisibleDisplayFrame(gameSize); +			final int keyboardHeight = fullSize.y - gameSize.bottom; +			GodotLib.setVirtualKeyboardHeight(keyboardHeight);  		}); -		mRenderView.queueOnRenderThread(new Runnable() { -			@Override -			public void run() { -				// Must occur after GodotLib.setup has completed. -				for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { -					plugin.onRegisterPluginWithGodotNative(); -				} - -				setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); +		mRenderView.queueOnRenderThread(() -> { +			for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { +				plugin.onRegisterPluginWithGodotNative();  			} +			setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));  		});  		// Include the returned non-null views in the Godot view hierarchy. @@ -316,14 +301,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	}  	public void setKeepScreenOn(final boolean p_enabled) { -		runOnUiThread(new Runnable() { -			@Override -			public void run() { -				if (p_enabled) { -					getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); -				} else { -					getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); -				} +		runOnUiThread(() -> { +			if (p_enabled) { +				getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); +			} else { +				getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  			}  		});  	} @@ -335,7 +317,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	@SuppressLint("MissingPermission")  	@Keep  	private void vibrate(int durationMs) { -		if (requestPermission("VIBRATE")) { +		if (durationMs > 0 && requestPermission("VIBRATE")) {  			Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);  			if (v != null) {  				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -349,42 +331,21 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	}  	public void restart() { -		// HACK: -		// -		// Currently it's very hard to properly deinitialize Godot on Android to restart the game -		// from scratch. Therefore, we need to kill the whole app process and relaunch it. -		// -		// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including -		// releasing and reloading native libs or resetting their state somehow and clearing statics). -		// -		// Using instrumentation is a way of making the whole app process restart, because Android -		// will kill any process of the same package which was already running. -		// -		final Activity activity = getActivity(); -		if (activity != null) { -			Bundle args = new Bundle(); -			args.putParcelable("intent", mCurrentIntent); -			activity.startInstrumentation(new ComponentName(activity, GodotInstrumentation.class), null, args); +		if (godotHost != null) { +			godotHost.onGodotRestartRequested(this);  		}  	}  	public void alert(final String message, final String title) {  		final Activity activity = getActivity(); -		runOnUiThread(new Runnable() { -			@Override -			public void run() { -				AlertDialog.Builder builder = new AlertDialog.Builder(activity); -				builder.setMessage(message).setTitle(title); -				builder.setPositiveButton( -						"OK", -						new DialogInterface.OnClickListener() { -							public void onClick(DialogInterface dialog, int id) { -								dialog.cancel(); -							} -						}); -				AlertDialog dialog = builder.create(); -				dialog.show(); -			} +		runOnUiThread(() -> { +			AlertDialog.Builder builder = new AlertDialog.Builder(activity); +			builder.setMessage(message).setTitle(title); +			builder.setPositiveButton( +					"OK", +					(dialog, id) -> dialog.cancel()); +			AlertDialog dialog = builder.create(); +			dialog.show();  		});  	} @@ -492,7 +453,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		final Activity activity = getActivity();  		io = new GodotIO(activity); -		io.unique_id = Secure.getString(activity.getContentResolver(), Secure.ANDROID_ID);  		GodotLib.io = io;  		netUtils = new GodotNetUtils(activity);  		mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE); @@ -534,7 +494,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		String main_pack_md5 = null;  		String main_pack_key = null; -		List<String> new_args = new LinkedList<String>(); +		List<String> new_args = new LinkedList<>();  		for (int i = 0; i < command_line.length; i++) {  			boolean has_extra = i < command_line.length - 1; @@ -678,8 +638,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		super.onDestroy(); -		// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each -		// native Godot components that is started in Godot#onVideoInit.  		forceQuit();  	} @@ -755,19 +713,16 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	public void UiChangeListener() {  		final View decorView = getActivity().getWindow().getDecorView(); -		decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { -			@Override -			public void onSystemUiVisibilityChange(int visibility) { -				if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { -					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { -						decorView.setSystemUiVisibility( -								View.SYSTEM_UI_FLAG_LAYOUT_STABLE | -								View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | -								View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | -								View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | -								View.SYSTEM_UI_FLAG_FULLSCREEN | -								View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); -					} +		decorView.setOnSystemUiVisibilityChangeListener(visibility -> { +			if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { +				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { +					decorView.setSystemUiVisibility( +							View.SYSTEM_UI_FLAG_LAYOUT_STABLE | +							View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | +							View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | +							View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | +							View.SYSTEM_UI_FLAG_FULLSCREEN | +							View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);  				}  			}  		}); @@ -780,7 +735,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		int displayRotation = display.getRotation();  		float[] adjustedValues = new float[3]; -		final int axisSwap[][] = { +		final int[][] axisSwap = {  			{ 1, -1, 0, 1 }, // ROTATION_0  			{ -1, -1, 1, 0 }, // ROTATION_90  			{ -1, 1, 0, 1 }, // ROTATION_180 @@ -798,21 +753,18 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		final int typeOfSensor = event.sensor.getType();  		if (mRenderView != null) { -			mRenderView.queueOnRenderThread(new Runnable() { -				@Override -				public void run() { -					if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { -						GodotLib.accelerometer(-x, y, -z); -					} -					if (typeOfSensor == Sensor.TYPE_GRAVITY) { -						GodotLib.gravity(-x, y, -z); -					} -					if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) { -						GodotLib.magnetometer(-x, y, -z); -					} -					if (typeOfSensor == Sensor.TYPE_GYROSCOPE) { -						GodotLib.gyroscope(x, -y, z); -					} +			mRenderView.queueOnRenderThread(() -> { +				if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { +					GodotLib.accelerometer(-x, y, -z); +				} +				if (typeOfSensor == Sensor.TYPE_GRAVITY) { +					GodotLib.gravity(-x, y, -z); +				} +				if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) { +					GodotLib.magnetometer(-x, y, -z); +				} +				if (typeOfSensor == Sensor.TYPE_GYROSCOPE) { +					GodotLib.gyroscope(x, -y, z);  				}  			});  		} @@ -847,12 +799,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		}  		if (shouldQuit && mRenderView != null) { -			mRenderView.queueOnRenderThread(new Runnable() { -				@Override -				public void run() { -					GodotLib.back(); -				} -			}); +			mRenderView.queueOnRenderThread(GodotLib::back);  		}  	} @@ -874,7 +821,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  	}  	private void forceQuit() { -		System.exit(0); +		// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each +		// native Godot components that is started in Godot#onVideoInit. +		if (godotHost != null) { +			godotHost.onGodotForceQuit(this); +		}  	}  	private boolean obbIsCorrupted(String f, String main_pack_md5) { @@ -897,7 +848,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  			byte[] messageDigest = complete.digest();  			// Create Hex String -			StringBuffer hexString = new StringBuffer(); +			StringBuilder hexString = new StringBuilder();  			for (int i = 0; i < messageDigest.length; i++) {  				String s = Integer.toHexString(0xFF & messageDigest[i]); @@ -918,33 +869,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  		}  	} -	public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { -		String s = event.getCharacters(); -		if (s == null || s.length() == 0) -			return false; - -		final char[] cc = s.toCharArray(); -		int cnt = 0; -		for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) -			; -		if (cnt == 0) -			return false; -		mRenderView.queueOnRenderThread(new Runnable() { -			// This method will be called on the rendering thread: -			public void run() { -				for (int i = 0, n = cc.length; i < n; i++) { -					int keyCode; -					if ((keyCode = cc[i]) != 0) { -						// Simulate key down and up... -						GodotLib.key(0, 0, keyCode, true); -						GodotLib.key(0, 0, keyCode, false); -					} -				} -			} -		}); -		return true; -	} -  	public boolean requestPermission(String p_name) {  		return PermissionsUtil.requestPermission(p_name, getActivity());  	} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 63c91561ff..a9d45c943b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -44,11 +44,15 @@ import android.annotation.SuppressLint;  import android.content.Context;  import android.graphics.PixelFormat;  import android.opengl.GLSurfaceView; +import android.os.Build;  import android.view.GestureDetector;  import android.view.KeyEvent;  import android.view.MotionEvent; +import android.view.PointerIcon;  import android.view.SurfaceView; +import androidx.annotation.Keep; +  /**   * A simple GLSurfaceView sub-class that demonstrate how to perform   * OpenGL ES 2.0 rendering into a GL Surface. Note the following important @@ -72,6 +76,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView  	private final GodotInputHandler inputHandler;  	private final GestureDetector detector;  	private final GodotRenderer godotRenderer; +	private PointerIcon pointerIcon;  	public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,  			boolean p_use_debug_opengl) { @@ -83,6 +88,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView  		this.inputHandler = new GodotInputHandler(this);  		this.detector = new GestureDetector(context, new GodotGestureHandler(this));  		this.godotRenderer = new GodotRenderer(); +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +			pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); +		}  		init(xrMode, false, 16, 0);  	} @@ -149,6 +157,21 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView  		return inputHandler.onGenericMotionEvent(event);  	} +	/** +	 * called from JNI to change pointer icon +	 */ +	@Keep +	public void setPointerIcon(int pointerType) { +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +			pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); +		} +	} + +	@Override +	public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { +		return pointerIcon; +	} +  	private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {  		setPreserveEGLContextOnPause(true);  		setFocusableInTouchMode(true); @@ -209,13 +232,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView  	public void onResume() {  		super.onResume(); -		queueEvent(new Runnable() { -			@Override -			public void run() { -				// Resume the renderer -				godotRenderer.onActivityResumed(); -				GodotLib.focusin(); -			} +		queueEvent(() -> { +			// Resume the renderer +			godotRenderer.onActivityResumed(); +			GodotLib.focusin();  		});  	} @@ -223,13 +243,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView  	public void onPause() {  		super.onPause(); -		queueEvent(new Runnable() { -			@Override -			public void run() { -				GodotLib.focusout(); -				// Pause the renderer -				godotRenderer.onActivityPaused(); -			} +		queueEvent(() -> { +			GodotLib.focusout(); +			// Pause the renderer +			godotRenderer.onActivityPaused();  		});  	}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 317fd13535..7b22895994 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -53,4 +53,15 @@ public interface GodotHost {  	 * Invoked on the render thread when the Godot main loop has started.  	 */  	default void onGodotMainLoopStarted() {} + +	/** +	 * Invoked on the UI thread as the last step of the Godot instance clean up phase. +	 */ +	default void onGodotForceQuit(Godot instance) {} + +	/** +	 * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host +	 * to perform the appropriate action(s). +	 */ +	default void onGodotRestartRequested(Godot instance) {}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index c7c7c1b40c..d85d88ec6c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -30,17 +30,19 @@  package org.godotengine.godot; -import org.godotengine.godot.input.*; +import org.godotengine.godot.input.GodotEditText;  import android.app.Activity; -import android.content.*; +import android.content.ActivityNotFoundException;  import android.content.Intent;  import android.content.pm.ActivityInfo;  import android.content.res.AssetManager;  import android.graphics.Point; -import android.media.*;  import android.net.Uri; -import android.os.*; +import android.os.Build; +import android.os.Environment; +import android.provider.Settings; +import android.text.TextUtils;  import android.util.DisplayMetrics;  import android.util.Log;  import android.util.SparseArray; @@ -49,14 +51,16 @@ import android.view.DisplayCutout;  import android.view.WindowInsets;  import java.io.IOException; -import java.io.InputStream;  import java.util.Locale;  // Wrapper for native library  public class GodotIO { -	AssetManager am; -	final Activity activity; +	private static final String TAG = GodotIO.class.getSimpleName(); + +	private final AssetManager am; +	private final Activity activity; +	private final String uniqueId;  	GodotEditText edit;  	final int SCREEN_LANDSCAPE = 0; @@ -68,179 +72,18 @@ public class GodotIO {  	final int SCREEN_SENSOR = 6;  	///////////////////////// -	/// FILES -	///////////////////////// - -	public int last_file_id = 1; - -	class AssetData { -		public boolean eof = false; -		public String path; -		public InputStream is; -		public int len; -		public int pos; -	} - -	SparseArray<AssetData> streams; - -	public int file_open(String path, boolean write) { -		//System.out.printf("file_open: Attempt to Open %s\n",path); - -		//Log.v("MyApp", "TRYING TO OPEN FILE: " + path); -		if (write) -			return -1; - -		AssetData ad = new AssetData(); - -		try { -			ad.is = am.open(path); - -		} catch (Exception e) { -			//System.out.printf("Exception on file_open: %s\n",path); -			return -1; -		} - -		try { -			ad.len = ad.is.available(); -		} catch (Exception e) { -			System.out.printf("Exception availabling on file_open: %s\n", path); -			return -1; -		} - -		ad.path = path; -		ad.pos = 0; -		++last_file_id; -		streams.put(last_file_id, ad); - -		return last_file_id; -	} -	public int file_get_size(int id) { -		if (streams.get(id) == null) { -			System.out.printf("file_get_size: Invalid file id: %d\n", id); -			return -1; -		} - -		return streams.get(id).len; -	} -	public void file_seek(int id, int bytes) { -		if (streams.get(id) == null) { -			System.out.printf("file_get_size: Invalid file id: %d\n", id); -			return; -		} -		//seek sucks -		AssetData ad = streams.get(id); -		if (bytes > ad.len) -			bytes = ad.len; -		if (bytes < 0) -			bytes = 0; - -		try { -			if (bytes > (int)ad.pos) { -				int todo = bytes - (int)ad.pos; -				while (todo > 0) { -					todo -= ad.is.skip(todo); -				} -				ad.pos = bytes; -			} else if (bytes < (int)ad.pos) { -				ad.is = am.open(ad.path); - -				ad.pos = bytes; -				int todo = bytes; -				while (todo > 0) { -					todo -= ad.is.skip(todo); -				} -			} - -			ad.eof = false; -		} catch (IOException e) { -			System.out.printf("Exception on file_seek: %s\n", e); -			return; -		} -	} - -	public int file_tell(int id) { -		if (streams.get(id) == null) { -			System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id); -			return 0; -		} - -		AssetData ad = streams.get(id); -		return ad.pos; -	} -	public boolean file_eof(int id) { -		if (streams.get(id) == null) { -			System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id); -			return false; -		} - -		AssetData ad = streams.get(id); -		return ad.eof; -	} - -	public byte[] file_read(int id, int bytes) { -		if (streams.get(id) == null) { -			System.out.printf("file_read: Can't read invalid file id: %d\n", id); -			return new byte[0]; -		} - -		AssetData ad = streams.get(id); - -		if (ad.pos + bytes > ad.len) { -			bytes = ad.len - ad.pos; -			ad.eof = true; -		} - -		if (bytes == 0) { -			return new byte[0]; -		} - -		byte[] buf1 = new byte[bytes]; -		int r = 0; -		try { -			r = ad.is.read(buf1); -		} catch (IOException e) { -			System.out.printf("Exception on file_read: %s\n", e); -			return new byte[bytes]; -		} - -		if (r == 0) { -			return new byte[0]; -		} - -		ad.pos += r; - -		if (r < bytes) { -			byte[] buf2 = new byte[r]; -			for (int i = 0; i < r; i++) -				buf2[i] = buf1[i]; -			return buf2; -		} else { -			return buf1; -		} -	} - -	public void file_close(int id) { -		if (streams.get(id) == null) { -			System.out.printf("file_close: Can't close invalid file id: %d\n", id); -			return; -		} - -		streams.remove(id); -	} - -	/////////////////////////  	/// DIRECTORIES  	///////////////////////// -	class AssetDir { +	static class AssetDir {  		public String[] files;  		public int current;  		public String path;  	} -	public int last_dir_id = 1; +	private int last_dir_id = 1; -	SparseArray<AssetDir> dirs; +	private final SparseArray<AssetDir> dirs;  	public int dir_open(String path) {  		AssetDir ad = new AssetDir(); @@ -259,7 +102,6 @@ public class GodotIO {  			return -1;  		} -		//System.out.printf("Opened dir: %s\n",path);  		++last_dir_id;  		dirs.put(last_dir_id, ad); @@ -322,98 +164,14 @@ public class GodotIO {  	GodotIO(Activity p_activity) {  		am = p_activity.getAssets();  		activity = p_activity; -		//streams = new HashMap<Integer, AssetData>(); -		streams = new SparseArray<AssetData>(); -		dirs = new SparseArray<AssetDir>(); -	} - -	///////////////////////// -	// AUDIO -	///////////////////////// - -	private Object buf; -	private Thread mAudioThread; -	private AudioTrack mAudioTrack; - -	public Object audioInit(int sampleRate, int desiredFrames) { -		int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; -		int audioFormat = AudioFormat.ENCODING_PCM_16BIT; -		int frameSize = 4; - -		System.out.printf("audioInit: initializing audio:\n"); - -		//Log.v("Godot", "Godot audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - -		// Let the user pick a larger buffer if they really want -- but ye -		// gods they probably shouldn't, the minimums are horrifyingly high -		// latency already -		desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); - -		mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, -				channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); - -		audioStartThread(); - -		//Log.v("Godot", "Godot audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - -		buf = new short[desiredFrames * 2]; -		return buf; -	} - -	public void audioStartThread() { -		mAudioThread = new Thread(new Runnable() { -			public void run() { -				mAudioTrack.play(); -				GodotLib.audio(); -			} -		}); - -		// I'd take REALTIME if I could get it! -		mAudioThread.setPriority(Thread.MAX_PRIORITY); -		mAudioThread.start(); -	} - -	public void audioWriteShortBuffer(short[] buffer) { -		for (int i = 0; i < buffer.length;) { -			int result = mAudioTrack.write(buffer, i, buffer.length - i); -			if (result > 0) { -				i += result; -			} else if (result == 0) { -				try { -					Thread.sleep(1); -				} catch (InterruptedException e) { -					// Nom nom -				} -			} else { -				Log.w("Godot", "Godot audio: error return from write(short)"); -				return; -			} +		dirs = new SparseArray<>(); +		String androidId = Settings.Secure.getString(activity.getContentResolver(), +				Settings.Secure.ANDROID_ID); +		if (androidId == null) { +			androidId = "";  		} -	} - -	public void audioQuit() { -		if (mAudioThread != null) { -			try { -				mAudioThread.join(); -			} catch (Exception e) { -				Log.v("Godot", "Problem stopping audio thread: " + e); -			} -			mAudioThread = null; - -			//Log.v("Godot", "Finished waiting for audio thread"); -		} - -		if (mAudioTrack != null) { -			mAudioTrack.stop(); -			mAudioTrack = null; -		} -	} -	public void audioPause(boolean p_pause) { -		if (p_pause) -			mAudioTrack.pause(); -		else -			mAudioTrack.play(); +		uniqueId = androidId;  	}  	///////////////////////// @@ -422,7 +180,6 @@ public class GodotIO {  	public int openURI(String p_uri) {  		try { -			Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri);  			String path = p_uri;  			String type = "";  			if (path.startsWith("/")) { @@ -448,6 +205,10 @@ public class GodotIO {  		}  	} +	public String getCacheDir() { +		return activity.getCacheDir().getAbsolutePath(); +	} +  	public String getDataDir() {  		return activity.getFilesDir().getAbsolutePath();  	} @@ -471,7 +232,7 @@ public class GodotIO {  		Point size = new Point();  		display.getRealSize(size); -		int result[] = { 0, 0, size.x, size.y }; +		int[] result = { 0, 0, size.x, size.y };  		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {  			WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();  			DisplayCutout cutout = insets.getDisplayCutout(); @@ -493,12 +254,12 @@ public class GodotIO {  		//InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);  		//inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); -	}; +	}  	public void hideKeyboard() {  		if (edit != null)  			edit.hideKeyboard(); -	}; +	}  	public void setScreenOrientation(int p_orientation) {  		switch (p_orientation) { @@ -543,51 +304,58 @@ public class GodotIO {  	public static final int SYSTEM_DIR_PICTURES = 6;  	public static final int SYSTEM_DIR_RINGTONES = 7; -	public String getSystemDir(int idx) { -		String what = ""; +	public String getSystemDir(int idx, boolean shared_storage) { +		String what;  		switch (idx) { -			case SYSTEM_DIR_DESKTOP: { -				//what=Environment.DIRECTORY_DOCUMENTS; -				what = Environment.DIRECTORY_DOWNLOADS; +			case SYSTEM_DIR_DESKTOP: +			default: { +				what = null; // This leads to the app specific external root directory.  			} break; +  			case SYSTEM_DIR_DCIM: {  				what = Environment.DIRECTORY_DCIM; -  			} break; +  			case SYSTEM_DIR_DOCUMENTS: { -				what = Environment.DIRECTORY_DOWNLOADS; -				//what=Environment.DIRECTORY_DOCUMENTS; +				what = Environment.DIRECTORY_DOCUMENTS;  			} break; +  			case SYSTEM_DIR_DOWNLOADS: {  				what = Environment.DIRECTORY_DOWNLOADS; -  			} break; +  			case SYSTEM_DIR_MOVIES: {  				what = Environment.DIRECTORY_MOVIES; -  			} break; +  			case SYSTEM_DIR_MUSIC: {  				what = Environment.DIRECTORY_MUSIC;  			} break; +  			case SYSTEM_DIR_PICTURES: {  				what = Environment.DIRECTORY_PICTURES;  			} break; +  			case SYSTEM_DIR_RINGTONES: {  				what = Environment.DIRECTORY_RINGTONES; -  			} break;  		} -		if (what.equals("")) -			return ""; -		return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath(); +		if (shared_storage) { +			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { +				Log.w(TAG, "Shared storage access is limited on Android 10 and higher."); +			} +			if (TextUtils.isEmpty(what)) { +				return Environment.getExternalStorageDirectory().getAbsolutePath(); +			} else { +				return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath(); +			} +		} else { +			return activity.getExternalFilesDir(what).getAbsolutePath(); +		}  	} -	protected static final String PREFS_FILE = "device_id.xml"; -	protected static final String PREFS_DEVICE_ID = "device_id"; - -	public static String unique_id = "";  	public String getUniqueID() { -		return unique_id; +		return uniqueId;  	}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 534a50e9ed..95870acda1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -34,7 +34,6 @@ import android.app.Activity;  import android.hardware.SensorEvent;  import android.view.Surface; -import javax.microedition.khronos.egl.EGLConfig;  import javax.microedition.khronos.opengles.GL10;  /** @@ -175,11 +174,6 @@ public class GodotLib {  	public static native void focusout();  	/** -	 * Invoked when the audio thread is started. -	 */ -	public static native void audio(); - -	/**  	 * Used to access Godot global properties.  	 * @param p_key Property key  	 * @return String value of the property diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 2047c88070..79007764a7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -35,16 +35,18 @@ import org.godotengine.godot.input.GodotInputHandler;  import android.view.SurfaceView;  public interface GodotRenderView { -	abstract public SurfaceView getView(); +	SurfaceView getView(); -	abstract public void initInputDevices(); +	void initInputDevices(); -	abstract public void queueOnRenderThread(Runnable event); +	void queueOnRenderThread(Runnable event); -	abstract public void onActivityPaused(); -	abstract public void onActivityResumed(); +	void onActivityPaused(); +	void onActivityResumed(); -	abstract public void onBackPressed(); +	void onBackPressed(); -	abstract public GodotInputHandler getInputHandler(); +	GodotInputHandler getInputHandler(); + +	void setPointerIcon(int pointerType);  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index 59bdbf7f8d..878a119c5c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -34,7 +34,6 @@ import org.godotengine.godot.plugin.GodotPlugin;  import org.godotengine.godot.plugin.GodotPluginRegistry;  import org.godotengine.godot.utils.GLUtils; -import android.content.Context;  import android.opengl.GLSurfaceView;  import javax.microedition.khronos.egl.EGLConfig; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 2e59dbc0d0..6fca7f2a57 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -37,17 +37,21 @@ import org.godotengine.godot.vulkan.VkSurfaceView;  import android.annotation.SuppressLint;  import android.content.Context; +import android.os.Build;  import android.view.GestureDetector; -import android.view.InputDevice;  import android.view.KeyEvent;  import android.view.MotionEvent; +import android.view.PointerIcon;  import android.view.SurfaceView; +import androidx.annotation.Keep; +  public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {  	private final Godot godot;  	private final GodotInputHandler mInputHandler;  	private final GestureDetector mGestureDetector;  	private final VkRenderer mRenderer; +	private PointerIcon pointerIcon;  	public GodotVulkanRenderView(Context context, Godot godot) {  		super(context); @@ -56,7 +60,9 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV  		mInputHandler = new GodotInputHandler(this);  		mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));  		mRenderer = new VkRenderer(); - +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +			pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); +		}  		setFocusableInTouchMode(true);  		startRenderer(mRenderer);  	} @@ -124,17 +130,29 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV  		return mInputHandler.onGenericMotionEvent(event);  	} +	/** +	 * called from JNI to change pointer icon +	 */ +	@Keep +	public void setPointerIcon(int pointerType) { +		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +			pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); +		} +	} + +	@Override +	public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { +		return pointerIcon; +	} +  	@Override  	public void onResume() {  		super.onResume(); -		queueOnVkThread(new Runnable() { -			@Override -			public void run() { -				// Resume the renderer -				mRenderer.onVkResume(); -				GodotLib.focusin(); -			} +		queueOnVkThread(() -> { +			// Resume the renderer +			mRenderer.onVkResume(); +			GodotLib.focusin();  		});  	} @@ -142,13 +160,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV  	public void onPause() {  		super.onPause(); -		queueOnVkThread(new Runnable() { -			@Override -			public void run() { -				GodotLib.focusout(); -				// Pause the renderer -				mRenderer.onVkPause(); -			} +		queueOnVkThread(() -> { +			GodotLib.focusout(); +			// Pause the renderer +			mRenderer.onVkPause();  		});  	}  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java index 2c39d06832..6b248fd034 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java @@ -75,12 +75,7 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener  		final int x = Math.round(event.getX());  		final int y = Math.round(event.getY());  		final int buttonMask = event.getButtonState(); -		queueEvent(new Runnable() { -			@Override -			public void run() { -				GodotLib.doubleTap(buttonMask, x, y); -			} -		}); +		GodotLib.doubleTap(buttonMask, x, y);  		return true;  	} @@ -89,12 +84,7 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener  		//Log.i("GodotGesture", "onScroll");  		final int x = Math.round(distanceX);  		final int y = Math.round(distanceY); -		queueEvent(new Runnable() { -			@Override -			public void run() { -				GodotLib.scroll(x, y); -			} -		}); +		GodotLib.scroll(x, y);  		return true;  	} diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 435b8b325f..fc0b84b392 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -45,13 +45,9 @@ import android.view.InputDevice.MotionRange;  import android.view.KeyEvent;  import android.view.MotionEvent; -import java.util.ArrayList;  import java.util.Collections;  import java.util.Comparator; -import java.util.HashMap;  import java.util.HashSet; -import java.util.List; -import java.util.Map;  import java.util.Set;  /** @@ -64,7 +60,7 @@ public class GodotInputHandler implements InputDeviceListener {  	private final String tag = this.getClass().getSimpleName();  	private final SparseIntArray mJoystickIds = new SparseIntArray(4); -	private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<Joystick>(4); +	private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);  	public GodotInputHandler(GodotRenderView godotView) {  		mRenderView = godotView; @@ -72,10 +68,6 @@ public class GodotInputHandler implements InputDeviceListener {  		mInputManager.registerInputDeviceListener(this, null);  	} -	private void queueEvent(Runnable task) { -		mRenderView.queueOnRenderThread(task); -	} -  	private boolean isKeyEvent_GameDevice(int source) {  		// Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD)  		if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD)) @@ -100,23 +92,12 @@ public class GodotInputHandler implements InputDeviceListener {  			if (mJoystickIds.indexOfKey(deviceId) >= 0) {  				final int button = getGodotButton(keyCode);  				final int godotJoyId = mJoystickIds.get(deviceId); - -				queueEvent(new Runnable() { -					@Override -					public void run() { -						GodotLib.joybutton(godotJoyId, button, false); -					} -				}); +				GodotLib.joybutton(godotJoyId, button, false);  			}  		} else {  			final int scanCode = event.getScanCode();  			final int chr = event.getUnicodeChar(0); -			queueEvent(new Runnable() { -				@Override -				public void run() { -					GodotLib.key(keyCode, scanCode, chr, false); -				} -			}); +			GodotLib.key(keyCode, scanCode, chr, false);  		}  		return true; @@ -146,23 +127,12 @@ public class GodotInputHandler implements InputDeviceListener {  			if (mJoystickIds.indexOfKey(deviceId) >= 0) {  				final int button = getGodotButton(keyCode);  				final int godotJoyId = mJoystickIds.get(deviceId); - -				queueEvent(new Runnable() { -					@Override -					public void run() { -						GodotLib.joybutton(godotJoyId, button, true); -					} -				}); +				GodotLib.joybutton(godotJoyId, button, true);  			}  		} else {  			final int scanCode = event.getScanCode();  			final int chr = event.getUnicodeChar(0); -			queueEvent(new Runnable() { -				@Override -				public void run() { -					GodotLib.key(keyCode, scanCode, chr, true); -				} -			}); +			GodotLib.key(keyCode, scanCode, chr, true);  		}  		return true; @@ -194,21 +164,16 @@ public class GodotInputHandler implements InputDeviceListener {  			final int action = event.getActionMasked();  			final int pointer_idx = event.getPointerId(event.getActionIndex()); -			mRenderView.queueOnRenderThread(new Runnable() { -				@Override -				public void run() { -					switch (action) { -						case MotionEvent.ACTION_DOWN: -						case MotionEvent.ACTION_CANCEL: -						case MotionEvent.ACTION_UP: -						case MotionEvent.ACTION_MOVE: -						case MotionEvent.ACTION_POINTER_UP: -						case MotionEvent.ACTION_POINTER_DOWN: { -							GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr); -						} break; -					} -				} -			}); +			switch (action) { +				case MotionEvent.ACTION_DOWN: +				case MotionEvent.ACTION_CANCEL: +				case MotionEvent.ACTION_UP: +				case MotionEvent.ACTION_MOVE: +				case MotionEvent.ACTION_POINTER_UP: +				case MotionEvent.ACTION_POINTER_DOWN: { +					GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr); +				} break; +			}  		}  		return true;  	} @@ -232,13 +197,7 @@ public class GodotInputHandler implements InputDeviceListener {  						// save value to prevent repeats  						joystick.axesValues.put(axis, value);  						final int godotAxisIdx = i; -						queueEvent(new Runnable() { -							@Override -							public void run() { -								GodotLib.joyaxis(godotJoyId, godotAxisIdx, value); -								//Log.i(tag, "GodotLib.joyaxis("+godotJoyId+", "+godotAxisIdx+", "+value+");"); -							} -						}); +						GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);  					}  				} @@ -248,13 +207,7 @@ public class GodotInputHandler implements InputDeviceListener {  					if (joystick.hatX != hatX || joystick.hatY != hatY) {  						joystick.hatX = hatX;  						joystick.hatY = hatY; -						queueEvent(new Runnable() { -							@Override -							public void run() { -								GodotLib.joyhat(godotJoyId, hatX, hatY); -								//Log.i(tag, "GodotLib.joyhat("+godotJoyId+", "+hatX+", "+hatY+");"); -							} -						}); +						GodotLib.joyhat(godotJoyId, hatX, hatY);  					}  				}  				return true; @@ -263,12 +216,7 @@ public class GodotInputHandler implements InputDeviceListener {  			final float x = event.getX();  			final float y = event.getY();  			final int type = event.getAction(); -			queueEvent(new Runnable() { -				@Override -				public void run() { -					GodotLib.hover(type, x, y); -				} -			}); +			GodotLib.hover(type, x, y);  			return true;  		} else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { @@ -333,7 +281,7 @@ public class GodotInputHandler implements InputDeviceListener {  		//Helps with creating new joypad mappings.  		Log.i(tag, "=== New Input Device: " + joystick.name); -		Set<Integer> already = new HashSet<Integer>(); +		Set<Integer> already = new HashSet<>();  		for (InputDevice.MotionRange range : device.getMotionRanges()) {  			boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);  			boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD); @@ -360,12 +308,7 @@ public class GodotInputHandler implements InputDeviceListener {  		}  		mJoysticksDevices.put(deviceId, joystick); -		queueEvent(new Runnable() { -			@Override -			public void run() { -				GodotLib.joyconnectionchanged(id, true, joystick.name); -			} -		}); +		GodotLib.joyconnectionchanged(id, true, joystick.name);  	}  	@Override @@ -377,13 +320,7 @@ public class GodotInputHandler implements InputDeviceListener {  		final int godotJoyId = mJoystickIds.get(deviceId);  		mJoystickIds.delete(deviceId);  		mJoysticksDevices.delete(deviceId); - -		queueEvent(new Runnable() { -			@Override -			public void run() { -				GodotLib.joyconnectionchanged(godotJoyId, false, ""); -			} -		}); +		GodotLib.joyconnectionchanged(godotJoyId, false, "");  	}  	@Override @@ -472,12 +409,7 @@ public class GodotInputHandler implements InputDeviceListener {  				final float x = event.getX();  				final float y = event.getY();  				final int type = event.getAction(); -				queueEvent(new Runnable() { -					@Override -					public void run() { -						GodotLib.hover(type, x, y); -					} -				}); +				GodotLib.hover(type, x, y);  				return true;  			}  			case MotionEvent.ACTION_BUTTON_PRESS: @@ -487,12 +419,7 @@ public class GodotInputHandler implements InputDeviceListener {  				final float y = event.getY();  				final int buttonsMask = event.getButtonState();  				final int action = event.getAction(); -				queueEvent(new Runnable() { -					@Override -					public void run() { -						GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask); -					} -				}); +				GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);  				return true;  			}  			case MotionEvent.ACTION_SCROLL: { @@ -502,12 +429,7 @@ public class GodotInputHandler implements InputDeviceListener {  				final int action = event.getAction();  				final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);  				final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); -				queueEvent(new Runnable() { -					@Override -					public void run() { -						GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor); -					} -				}); +				GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);  			}  			case MotionEvent.ACTION_DOWN:  			case MotionEvent.ACTION_UP: { diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index 3e0e6a65fd..002a75277d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -94,20 +94,15 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene  	public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {  		//Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); -		mRenderView.queueOnRenderThread(new Runnable() { -			@Override -			public void run() { -				for (int i = 0; i < count; ++i) { -					GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true); -					GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false); - -					if (mHasSelection) { -						mHasSelection = false; -						break; -					} -				} +		for (int i = 0; i < count; ++i) { +			GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true); +			GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false); + +			if (mHasSelection) { +				mHasSelection = false; +				break;  			} -		}); +		}  	}  	@Override @@ -118,20 +113,15 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene  		for (int i = start; i < start + count; ++i) {  			newChars[i - start] = pCharSequence.charAt(i);  		} -		mRenderView.queueOnRenderThread(new Runnable() { -			@Override -			public void run() { -				for (int i = 0; i < count; ++i) { -					int key = newChars[i]; -					if ((key == '\n') && !mEdit.isMultiline()) { -						// Return keys are handled through action events -						continue; -					} -					GodotLib.key(0, 0, key, true); -					GodotLib.key(0, 0, key, false); -				} +		for (int i = 0; i < count; ++i) { +			int key = newChars[i]; +			if ((key == '\n') && !mEdit.isMultiline()) { +				// Return keys are handled through action events +				continue;  			} -		}); +			GodotLib.key(0, 0, key, true); +			GodotLib.key(0, 0, key, false); +		}  	}  	@Override @@ -139,23 +129,19 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene  		if (mEdit == pTextView && isFullScreenEdit()) {  			final String characters = pKeyEvent.getCharacters(); -			mRenderView.queueOnRenderThread(new Runnable() { -				@Override -				public void run() { -					for (int i = 0; i < characters.length(); i++) { -						final int ch = characters.codePointAt(i); -						GodotLib.key(0, 0, ch, true); -						GodotLib.key(0, 0, ch, false); -					} -				} -			}); +			for (int i = 0; i < characters.length(); i++) { +				final int ch = characters.codePointAt(i); +				GodotLib.key(0, 0, ch, true); +				GodotLib.key(0, 0, ch, false); +			}  		}  		if (pActionID == EditorInfo.IME_ACTION_DONE) {  			// Enter key has been pressed -			GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); -			GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false); - +			mRenderView.queueOnRenderThread(() -> { +				GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); +				GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false); +			});  			mRenderView.getView().requestFocus();  			return true;  		} diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java index 62810ad3a4..21fdc658bb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java @@ -28,14 +28,14 @@ public interface InputManagerCompat {  	 * @param id The device id  	 * @return The input device or null if not found  	 */ -	public InputDevice getInputDevice(int id); +	InputDevice getInputDevice(int id);  	/**  	 * Gets the ids of all input devices in the system.  	 *  	 * @return The input device ids.  	 */ -	public int[] getInputDeviceIds(); +	int[] getInputDeviceIds();  	/**  	 * Registers an input device listener to receive notifications about when @@ -46,7 +46,7 @@ public interface InputManagerCompat {  	 *            null if the listener should be invoked on the calling thread's  	 *            looper.  	 */ -	public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener, +	void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,  			Handler handler);  	/** @@ -54,7 +54,7 @@ public interface InputManagerCompat {  	 *  	 * @param listener The listener to unregister.  	 */ -	public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener); +	void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);  	/*  	 * The following three calls are to simulate V16 behavior on pre-Jellybean @@ -69,7 +69,7 @@ public interface InputManagerCompat {  	 *  	 * @param event the motion event from the app  	 */ -	public void onGenericMotionEvent(MotionEvent event); +	void onGenericMotionEvent(MotionEvent event);  	/**  	 * Tell the V9 input manager that it should stop polling for disconnected @@ -77,7 +77,7 @@ public interface InputManagerCompat {  	 * might want to call it whenever your game is not active (or whenever you  	 * don't care about being notified of new input devices)  	 */ -	public void onPause(); +	void onPause();  	/**  	 * Tell the V9 input manager that it should start polling for disconnected @@ -85,9 +85,9 @@ public interface InputManagerCompat {  	 * might want to call it less often (only when the gameplay is actually  	 * active)  	 */ -	public void onResume(); +	void onResume(); -	public interface InputDeviceListener { +	interface InputDeviceListener {  		/**  		 * Called whenever the input manager detects that a device has been  		 * added. This will only be called in the V9 version when a motion event @@ -119,7 +119,7 @@ public interface InputManagerCompat {  	/**  	 * Use this to construct a compatible InputManager.  	 */ -	public static class Factory { +	class Factory {  		/**  		 * Constructs and returns a compatible InputManger  		 * diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java index 61828dccae..0dbc13c77b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java @@ -34,7 +34,7 @@ public class InputManagerV16 implements InputManagerCompat {  	public InputManagerV16(Context context) {  		mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE); -		mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>(); +		mListeners = new HashMap<>();  	}  	@Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java index 4b7318c718..bff90d7ce9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java @@ -41,12 +41,12 @@ import java.util.List;  class Joystick {  	int device_id;  	String name; -	List<Integer> axes = new ArrayList<Integer>(); +	List<Integer> axes = new ArrayList<>();  	protected boolean hasAxisHat = false;  	/*  	 * Keep track of values so we can prevent flooding the engine with useless events.  	 */ -	protected final SparseArray axesValues = new SparseArray<Float>(4); +	protected final SparseArray<Float> axesValues = new SparseArray<>(4);  	protected int hatX;  	protected int hatY;  } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index 6c8a3d4219..2dc8359615 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -136,7 +136,7 @@ public abstract class GodotPlugin {  		nativeRegisterSingleton(pluginName, pluginObject);  		Set<Method> filteredMethods = new HashSet<>(); -		Class clazz = pluginObject.getClass(); +		Class<?> clazz = pluginObject.getClass();  		Method[] methods = clazz.getDeclaredMethods();  		for (Method method : methods) { @@ -157,8 +157,8 @@ public abstract class GodotPlugin {  		for (Method method : filteredMethods) {  			List<String> ptr = new ArrayList<>(); -			Class[] paramTypes = method.getParameterTypes(); -			for (Class c : paramTypes) { +			Class<?>[] paramTypes = method.getParameterTypes(); +			for (Class<?> c : paramTypes) {  				ptr.add(c.getName());  			} diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java index d6e49bb635..2b6e4ad2be 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java @@ -39,10 +39,10 @@ public class Crypt {  			// Create MD5 Hash  			MessageDigest digest = java.security.MessageDigest.getInstance("MD5");  			digest.update(input.getBytes()); -			byte messageDigest[] = digest.digest(); +			byte[] messageDigest = digest.digest();  			// Create Hex String -			StringBuffer hexString = new StringBuffer(); +			StringBuilder hexString = new StringBuilder();  			for (int i = 0; i < messageDigest.length; i++)  				hexString.append(Integer.toHexString(0xFF & messageDigest[i]));  			return hexString.toString(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt index f0e37d80b8..b01dc2653a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt @@ -115,7 +115,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf  	/**  	 * Tear down the rendering thread.  	 * -	 * Must not be called  before a [VkRenderer] has been set. +	 * Must not be called before a [VkRenderer] has been set.  	 */  	fun onDestroy() {  		vkThread.blockingExit() diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt index b967fd5f24..6e59268076 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt @@ -61,6 +61,7 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk  	private var rendererInitialized = false  	private var rendererResumed = false  	private var resumed = false +	private var surfaceChanged = false  	private var hasSurface = false  	private var width = 0  	private var height = 0 @@ -141,8 +142,10 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk  	fun onSurfaceChanged(width: Int, height: Int) {  		lock.withLock {  			hasSurface = true +			surfaceChanged = true;  			this.width = width  			this.height = height +  			lockCondition.signalAll()  		}  	} @@ -188,8 +191,11 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk  									rendererInitialized = true  									vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)  								} +							} +							if (surfaceChanged) {  								vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height) +								surfaceChanged = false  							}  							// Break out of the loop so drawing can occur without holding onto the lock. diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java index 71610d2d00..c852e8759a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -30,7 +30,6 @@  package org.godotengine.godot.xr.regular; -import org.godotengine.godot.GodotLib;  import org.godotengine.godot.utils.GLUtils;  import android.opengl.GLSurfaceView;  |