Enable the ability to use Godot as a subview within an Android app

This commit is contained in:
Fredia Huya-Kouadio 2020-06-15 00:46:13 -07:00
parent 23f7f86914
commit 175d43738a
17 changed files with 345 additions and 229 deletions

View File

@ -30,11 +30,11 @@
package com.godot.game; package com.godot.game;
import org.godotengine.godot.Godot; import org.godotengine.godot.FullScreenGodotApp;
/** /**
* Template activity for Godot Android custom builds. * Template activity for Godot Android custom builds.
* Feel free to extend and modify this class for your custom logic. * Feel free to extend and modify this class for your custom logic.
*/ */
public class GodotApp extends Godot { public class GodotApp extends FullScreenGodotApp {
} }

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/godot_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -0,0 +1,79 @@
/*************************************************************************/
/* FullScreenGodotApp.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
package org.godotengine.godot;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import androidx.fragment.app.FragmentActivity;
/**
* Base activity for Android apps intending to use Godot as the primary and only screen.
*
* It's also a reference implementation for how to setup and use the {@link Godot} fragment
* within an Android app.
*/
public abstract class FullScreenGodotApp extends FragmentActivity {
protected Godot godotFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.godot_app_layout);
godotFragment = new Godot();
getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();
}
@Override
public void onNewIntent(Intent intent) {
if (godotFragment != null) {
godotFragment.onNewIntent(intent);
}
}
@Override
public void onBackPressed() {
if (godotFragment != null) {
godotFragment.onBackPressed();
} else {
super.onBackPressed();
}
}
@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);
}
}

View File

@ -30,6 +30,9 @@
package org.godotengine.godot; package org.godotengine.godot;
import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.WINDOW_SERVICE;
import org.godotengine.godot.input.GodotEditText; import org.godotengine.godot.input.GodotEditText;
import org.godotengine.godot.plugin.GodotPlugin; import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry; import org.godotengine.godot.plugin.GodotPluginRegistry;
@ -68,6 +71,7 @@ import android.os.Vibrator;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.view.Display; import android.view.Display;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface; import android.view.Surface;
import android.view.View; import android.view.View;
@ -84,7 +88,7 @@ import android.widget.TextView;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.Fragment;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo; import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
@ -102,7 +106,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient { public class Godot extends Fragment implements SensorEventListener, IDownloaderClient {
private IStub mDownloaderClientStub; private IStub mDownloaderClientStub;
private TextView mStatusText; private TextView mStatusText;
private TextView mProgressFraction; private TextView mProgressFraction;
@ -130,7 +134,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
static private Intent mCurrentIntent; static private Intent mCurrentIntent;
@Override
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
mCurrentIntent = intent; mCurrentIntent = intent;
} }
@ -156,6 +159,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
private String[] command_line; private String[] command_line;
private boolean use_apk_expansion; private boolean use_apk_expansion;
private ViewGroup containerLayout;
public GodotRenderView mRenderView; public GodotRenderView mRenderView;
private boolean godot_initialized = false; private boolean godot_initialized = false;
@ -174,7 +178,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
public ResultCallback result_callback; public ResultCallback result_callback;
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (result_callback != null) { if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data); result_callback.callback(requestCode, resultCode, data);
result_callback = null; result_callback = null;
@ -211,27 +215,28 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
*/ */
@Keep @Keep
private void onVideoInit() { private void onVideoInit() {
final FrameLayout layout = new FrameLayout(this); final Activity activity = getActivity();
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); containerLayout = new FrameLayout(activity);
setContentView(layout); containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// GodotEditText layout // GodotEditText layout
GodotEditText editText = new GodotEditText(this); GodotEditText editText = new GodotEditText(activity);
editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
// ...add to FrameLayout // ...add to FrameLayout
layout.addView(editText); containerLayout.addView(editText);
GodotLib.setup(command_line); GodotLib.setup(command_line);
final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name"); final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name");
if (videoDriver.equals("Vulkan")) { if (videoDriver.equals("Vulkan")) {
mRenderView = new GodotVulkanRenderView(this); mRenderView = new GodotVulkanRenderView(activity, this);
} else { } else {
mRenderView = new GodotGLRenderView(this, xrMode, use_32_bits, use_debug_opengl); mRenderView = new GodotGLRenderView(activity, this, xrMode, use_32_bits,
use_debug_opengl);
} }
View view = mRenderView.getView(); View view = mRenderView.getView();
layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); containerLayout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
editText.setView(mRenderView); editText.setView(mRenderView);
io.setEdit(editText); io.setEdit(editText);
@ -239,7 +244,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override @Override
public void onGlobalLayout() { public void onGlobalLayout() {
Point fullSize = new Point(); Point fullSize = new Point();
getWindowManager().getDefaultDisplay().getSize(fullSize); activity.getWindowManager().getDefaultDisplay().getSize(fullSize);
Rect gameSize = new Rect(); Rect gameSize = new Rect();
mRenderView.getView().getWindowVisibleDisplayFrame(gameSize); mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
@ -262,9 +267,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
// Include the returned non-null views in the Godot view hierarchy. // Include the returned non-null views in the Godot view hierarchy.
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
View pluginView = plugin.onMainCreate(this); View pluginView = plugin.onMainCreate(activity);
if (pluginView != null) { if (pluginView != null) {
layout.addView(pluginView); containerLayout.addView(pluginView);
} }
} }
} }
@ -274,9 +279,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override @Override
public void run() { public void run() {
if (p_enabled) { if (p_enabled) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else { } else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} }
} }
}); });
@ -290,7 +295,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Keep @Keep
private void vibrate(int durationMs) { private void vibrate(int durationMs) {
if (requestPermission("VIBRATE")) { if (requestPermission("VIBRATE")) {
Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) { if (v != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE)); v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE));
@ -314,13 +319,16 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
// Using instrumentation is a way of making the whole app process restart, because Android // 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. // will kill any process of the same package which was already running.
// //
Bundle args = new Bundle(); final Activity activity = getActivity();
args.putParcelable("intent", mCurrentIntent); if (activity != null) {
startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args); Bundle args = new Bundle();
args.putParcelable("intent", mCurrentIntent);
activity.startInstrumentation(new ComponentName(activity, GodotInstrumentation.class), null, args);
}
} }
public void alert(final String message, final String title) { public void alert(final String message, final String title) {
final Activity activity = this; final Activity activity = getActivity();
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -340,7 +348,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
public int getGLESVersionCode() { public int getGLESVersionCode() {
ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo(); ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo();
return deviceInfo.reqGlEsVersion; return deviceInfo.reqGlEsVersion;
} }
@ -349,7 +357,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
protected String[] getCommandLine() { protected String[] getCommandLine() {
InputStream is; InputStream is;
try { try {
is = getAssets().open("_cl_"); is = getActivity().getAssets().open("_cl_");
byte[] len = new byte[4]; byte[] len = new byte[4];
int r = is.read(len); int r = is.read(len);
if (r < 4) { if (r < 4) {
@ -426,11 +434,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
command_line = new_cmdline; command_line = new_cmdline;
} }
io = new GodotIO(this); final Activity activity = getActivity();
io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); io = new GodotIO(activity);
io.unique_id = Secure.getString(activity.getContentResolver(), Secure.ANDROID_ID);
GodotLib.io = io; GodotLib.io = io;
netUtils = new GodotNetUtils(this); netUtils = new GodotNetUtils(activity);
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
@ -440,7 +449,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
GodotLib.initialize(this, getAssets(), use_apk_expansion); GodotLib.initialize(activity, this, activity.getAssets(), use_apk_expansion);
result_callback = null; result_callback = null;
@ -454,151 +463,152 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
@Override @Override
protected void onCreate(Bundle icicle) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
super.onCreate(icicle); final Activity activity = getActivity();
Window window = getWindow(); Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE);
pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this); pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
//check for apk expansion API //check for apk expansion API
if (true) { boolean md5mismatch = false;
boolean md5mismatch = false; command_line = getCommandLine();
command_line = getCommandLine(); String main_pack_md5 = null;
String main_pack_md5 = null; String main_pack_key = null;
String main_pack_key = null;
List<String> new_args = new LinkedList<String>(); List<String> new_args = new LinkedList<String>();
for (int i = 0; i < command_line.length; i++) { for (int i = 0; i < command_line.length; i++) {
boolean has_extra = i < command_line.length - 1; boolean has_extra = i < command_line.length - 1;
if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) { if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) {
xrMode = XRMode.REGULAR; xrMode = XRMode.REGULAR;
} else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) { } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) {
xrMode = XRMode.OVR; xrMode = XRMode.OVR;
} else if (command_line[i].equals("--use_depth_32")) { } else if (command_line[i].equals("--use_depth_32")) {
use_32_bits = true; use_32_bits = true;
} else if (command_line[i].equals("--debug_opengl")) { } else if (command_line[i].equals("--debug_opengl")) {
use_debug_opengl = true; use_debug_opengl = true;
} else if (command_line[i].equals("--use_immersive")) { } else if (command_line[i].equals("--use_immersive")) {
use_immersive = true; use_immersive = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
window.getDecorView().setSystemUiVisibility( window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
UiChangeListener(); UiChangeListener();
}
} else if (command_line[i].equals("--use_apk_expansion")) {
use_apk_expansion = true;
} else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
main_pack_md5 = command_line[i + 1];
i++;
} else if (has_extra && command_line[i].equals("--apk_expansion_key")) {
main_pack_key = command_line[i + 1];
SharedPreferences prefs = getSharedPreferences("app_data_keys", MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("store_public_key", main_pack_key);
editor.apply();
i++;
} else if (command_line[i].trim().length() != 0) {
new_args.add(command_line[i]);
} }
} else if (command_line[i].equals("--use_apk_expansion")) {
use_apk_expansion = true;
} else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
main_pack_md5 = command_line[i + 1];
i++;
} else if (has_extra && command_line[i].equals("--apk_expansion_key")) {
main_pack_key = command_line[i + 1];
SharedPreferences prefs = activity.getSharedPreferences("app_data_keys",
MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("store_public_key", main_pack_key);
editor.apply();
i++;
} else if (command_line[i].trim().length() != 0) {
new_args.add(command_line[i]);
}
}
if (new_args.isEmpty()) {
command_line = null;
} else {
command_line = new_args.toArray(new String[new_args.size()]);
}
if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
//check that environment is ok!
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//show popup and die
} }
if (new_args.isEmpty()) { // Build the full path to the app's expansion files
command_line = null; try {
} else { expansion_pack_path = Helpers.getSaveFilePath(getContext());
command_line = new_args.toArray(new String[new_args.size()]); expansion_pack_path += "/main." + activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode + "." + activity.getPackageName() + ".obb";
} catch (Exception e) {
e.printStackTrace();
} }
if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
//check that environment is ok!
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//show popup and die
}
// Build the full path to the app's expansion files File f = new File(expansion_pack_path);
boolean pack_valid = true;
if (!f.exists()) {
pack_valid = false;
} else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) {
pack_valid = false;
try { try {
expansion_pack_path = Helpers.getSaveFilePath(getApplicationContext()); f.delete();
expansion_pack_path += "/main." + getPackageManager().getPackageInfo(getPackageName(), 0).versionCode + "." + this.getPackageName() + ".obb";
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
} }
}
File f = new File(expansion_pack_path); if (!pack_valid) {
Intent notifierIntent = new Intent(activity, activity.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
boolean pack_valid = true; PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (!f.exists()) { int startResult;
pack_valid = false; try {
startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
getContext(),
pendingIntent,
GodotDownloaderService.class);
} else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) { if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
pack_valid = false; // This is where you do set up to display the download
try { // progress (next step)
f.delete(); mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
} catch (Exception e) {
}
}
if (!pack_valid) {
Intent notifierIntent = new Intent(this, this.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
int startResult;
try {
startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
getApplicationContext(),
pendingIntent,
GodotDownloaderService.class); GodotDownloaderService.class);
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { View downloadingExpansionView =
// This is where you do set up to display the download inflater.inflate(R.layout.downloading_expansion, container, false);
// progress (next step) mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
GodotDownloaderService.class); mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
setContentView(R.layout.downloading_expansion); return downloadingExpansionView;
mPB = (ProgressBar)findViewById(R.id.progressBar);
mStatusText = (TextView)findViewById(R.id.statusText);
mProgressFraction = (TextView)findViewById(R.id.progressAsFraction);
mProgressPercent = (TextView)findViewById(R.id.progressAsPercentage);
mAverageSpeed = (TextView)findViewById(R.id.progressAverageSpeed);
mTimeRemaining = (TextView)findViewById(R.id.progressTimeRemaining);
mDashboard = findViewById(R.id.downloaderDashboard);
mCellMessage = findViewById(R.id.approveCellular);
mPauseButton = (Button)findViewById(R.id.pauseButton);
mWiFiSettingsButton = (Button)findViewById(R.id.wifiSettingsButton);
return;
}
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
} }
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
} }
} }
} }
mCurrentIntent = getIntent(); mCurrentIntent = activity.getIntent();
initializeGodot(); initializeGodot();
return containerLayout;
} }
@Override @Override
protected void onDestroy() { public void onDestroy() {
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainDestroy(); plugin.onMainDestroy();
} }
GodotLib.ondestroy(this); GodotLib.ondestroy();
super.onDestroy(); super.onDestroy();
@ -608,13 +618,13 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
@Override @Override
protected void onPause() { public void onPause() {
super.onPause(); super.onPause();
activityResumed = false; activityResumed = false;
if (!godot_initialized) { if (!godot_initialized) {
if (null != mDownloaderClientStub) { if (null != mDownloaderClientStub) {
mDownloaderClientStub.disconnect(this); mDownloaderClientStub.disconnect(getActivity());
} }
return; return;
} }
@ -644,12 +654,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
@Override @Override
protected void onResume() { public void onResume() {
super.onResume(); super.onResume();
activityResumed = true; activityResumed = true;
if (!godot_initialized) { if (!godot_initialized) {
if (null != mDownloaderClientStub) { if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this); mDownloaderClientStub.connect(getActivity());
} }
return; return;
} }
@ -662,7 +672,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
if (use_immersive && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+ if (use_immersive && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
Window window = getWindow(); Window window = getActivity().getWindow();
window.getDecorView().setSystemUiVisibility( window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
@ -678,7 +688,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
public void UiChangeListener() { public void UiChangeListener() {
final View decorView = getWindow().getDecorView(); final View decorView = getActivity().getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override @Override
public void onSystemUiVisibilityChange(int visibility) { public void onSystemUiVisibilityChange(int visibility) {
@ -699,7 +709,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override @Override
public void onSensorChanged(SensorEvent event) { public void onSensorChanged(SensorEvent event) {
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); Display display =
((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int displayRotation = display.getRotation(); int displayRotation = display.getRotation();
float[] adjustedValues = new float[3]; float[] adjustedValues = new float[3];
@ -762,7 +773,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
*/ */
@Override
public void onBackPressed() { public void onBackPressed() {
boolean shouldQuit = true; boolean shouldQuit = true;
@ -793,6 +803,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
} }
public final void runOnUiThread(@NonNull Runnable action) {
if (getActivity() != null) {
getActivity().runOnUiThread(action);
}
}
private void forceQuit() { private void forceQuit() {
System.exit(0); System.exit(0);
} }
@ -895,18 +911,17 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
return true; return true;
} }
@Override
public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
String s = event.getCharacters(); String s = event.getCharacters();
if (s == null || s.length() == 0) if (s == null || s.length() == 0)
return super.onKeyMultiple(inKeyCode, repeatCount, event); return false;
final char[] cc = s.toCharArray(); final char[] cc = s.toCharArray();
int cnt = 0; int cnt = 0;
for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0)
; ;
if (cnt == 0) if (cnt == 0)
return super.onKeyMultiple(inKeyCode, repeatCount, event); return false;
mRenderView.queueOnRenderThread(new Runnable() { mRenderView.queueOnRenderThread(new Runnable() {
// This method will be called on the rendering thread: // This method will be called on the rendering thread:
public void run() { public void run() {
@ -924,15 +939,15 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
} }
public boolean requestPermission(String p_name) { public boolean requestPermission(String p_name) {
return PermissionsUtil.requestPermission(p_name, this); return PermissionsUtil.requestPermission(p_name, getActivity());
} }
public boolean requestPermissions() { public boolean requestPermissions() {
return PermissionsUtil.requestManifestPermissions(this); return PermissionsUtil.requestManifestPermissions(getActivity());
} }
public String[] getGrantedPermissions() { public String[] getGrantedPermissions() {
return PermissionsUtil.getGrantedPermissions(this); return PermissionsUtil.getGrantedPermissions(getActivity());
} }
/** /**

View File

@ -42,6 +42,7 @@ import org.godotengine.godot.xr.regular.RegularContextFactory;
import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.view.GestureDetector; import android.view.GestureDetector;
@ -68,19 +69,20 @@ import android.view.SurfaceView;
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error. * bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
*/ */
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot activity; private final Godot godot;
private final GodotInputHandler inputHandler; private final GodotInputHandler inputHandler;
private final GestureDetector detector; private final GestureDetector detector;
private final GodotRenderer godotRenderer; private final GodotRenderer godotRenderer;
public GodotGLRenderView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) { public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
super(activity); boolean p_use_debug_opengl) {
super(context);
GLUtils.use_32 = p_use_32_bits; GLUtils.use_32 = p_use_32_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl; GLUtils.use_debug_opengl = p_use_debug_opengl;
this.activity = activity; this.godot = godot;
this.inputHandler = new GodotInputHandler(this); this.inputHandler = new GodotInputHandler(this);
this.detector = new GestureDetector(activity, new GodotGestureHandler(this)); this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer(); this.godotRenderer = new GodotRenderer();
init(xrMode, false, 16, 0); init(xrMode, false, 16, 0);
} }
@ -112,7 +114,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override @Override
public void onBackPressed() { public void onBackPressed() {
activity.onBackPressed(); godot.onBackPressed();
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@ -120,7 +122,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); super.onTouchEvent(event);
this.detector.onTouchEvent(event); this.detector.onTouchEvent(event);
return activity.gotTouchEvent(event); return godot.gotTouchEvent(event);
} }
@Override @Override

View File

@ -32,6 +32,7 @@ package org.godotengine.godot;
import org.godotengine.godot.input.*; import org.godotengine.godot.input.*;
import android.app.Activity;
import android.content.*; import android.content.*;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
@ -51,7 +52,7 @@ import java.util.Locale;
public class GodotIO { public class GodotIO {
AssetManager am; AssetManager am;
Godot activity; final Activity activity;
GodotEditText edit; GodotEditText edit;
final int SCREEN_LANDSCAPE = 0; final int SCREEN_LANDSCAPE = 0;
@ -314,7 +315,7 @@ public class GodotIO {
dirs.remove(id); dirs.remove(id);
} }
GodotIO(Godot p_activity) { GodotIO(Activity p_activity) {
am = p_activity.getAssets(); am = p_activity.getAssets();
activity = p_activity; activity = p_activity;
//streams = new HashMap<Integer, AssetData>(); //streams = new HashMap<Integer, AssetData>();

View File

@ -50,13 +50,13 @@ public class GodotLib {
/** /**
* Invoked on the main thread to initialize Godot native layer. * Invoked on the main thread to initialize Godot native layer.
*/ */
public static native void initialize(Godot p_instance, Object p_asset_manager, boolean use_apk_expansion); public static native void initialize(Activity activity, Godot p_instance, Object p_asset_manager, boolean use_apk_expansion);
/** /**
* Invoked on the main thread to clean up Godot native layer. * Invoked on the main thread to clean up Godot native layer.
* @see Activity#onDestroy() * @see androidx.fragment.app.Fragment#onDestroy()
*/ */
public static native void ondestroy(Godot p_instance); public static native void ondestroy();
/** /**
* Invoked on the GL thread to complete setup for the Godot native layer logic. * Invoked on the GL thread to complete setup for the Godot native layer logic.
@ -161,14 +161,14 @@ public class GodotLib {
public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name); public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name);
/** /**
* Invoked when the Android activity resumes. * Invoked when the Android app resumes.
* @see Activity#onResume() * @see androidx.fragment.app.Fragment#onResume()
*/ */
public static native void focusin(); public static native void focusin();
/** /**
* Invoked when the Android activity pauses. * Invoked when the Android app pauses.
* @see Activity#onPause() * @see androidx.fragment.app.Fragment#onPause()
*/ */
public static native void focusout(); public static native void focusout();

View File

@ -36,23 +36,24 @@ import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView; import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.SurfaceView; import android.view.SurfaceView;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot mActivity; private final Godot godot;
private final GodotInputHandler mInputHandler; private final GodotInputHandler mInputHandler;
private final GestureDetector mGestureDetector; private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer; private final VkRenderer mRenderer;
public GodotVulkanRenderView(Godot activity) { public GodotVulkanRenderView(Context context, Godot godot) {
super(activity); super(context);
mActivity = activity; this.godot = godot;
mInputHandler = new GodotInputHandler(this); mInputHandler = new GodotInputHandler(this);
mGestureDetector = new GestureDetector(mActivity, new GodotGestureHandler(this)); mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer(); mRenderer = new VkRenderer();
setFocusableInTouchMode(true); setFocusableInTouchMode(true);
@ -86,7 +87,7 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override @Override
public void onBackPressed() { public void onBackPressed() {
mActivity.onBackPressed(); godot.onBackPressed();
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@ -94,7 +95,7 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); super.onTouchEvent(event);
mGestureDetector.onTouchEvent(event); mGestureDetector.onTouchEvent(event);
return mActivity.gotTouchEvent(event); return godot.gotTouchEvent(event);
} }
@Override @Override

View File

@ -98,7 +98,7 @@ public abstract class GodotPlugin {
*/ */
@Nullable @Nullable
protected Activity getActivity() { protected Activity getActivity() {
return godot; return godot.getActivity();
} }
/** /**

View File

@ -32,6 +32,7 @@ package org.godotengine.godot.plugin;
import org.godotengine.godot.Godot; import org.godotengine.godot.Godot;
import android.app.Activity;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
@ -121,9 +122,11 @@ public final class GodotPluginRegistry {
private void loadPlugins(Godot godot) { private void loadPlugins(Godot godot) {
try { try {
ApplicationInfo appInfo = godot final Activity activity = godot.getActivity();
ApplicationInfo appInfo = activity
.getPackageManager() .getPackageManager()
.getApplicationInfo(godot.getPackageName(), PackageManager.GET_META_DATA); .getApplicationInfo(activity.getPackageName(),
PackageManager.GET_META_DATA);
Bundle metaData = appInfo.metaData; Bundle metaData = appInfo.metaData;
if (metaData == null || metaData.isEmpty()) { if (metaData == null || metaData.isEmpty()) {
return; return;

View File

@ -30,8 +30,7 @@
package org.godotengine.godot.utils; package org.godotengine.godot.utils;
import org.godotengine.godot.Godot; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.util.Log; import android.util.Log;
@ -45,7 +44,7 @@ public class GodotNetUtils {
/* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */ /* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */
private WifiManager.MulticastLock multicastLock; private WifiManager.MulticastLock multicastLock;
public GodotNetUtils(Godot p_activity) { public GodotNetUtils(Activity p_activity) {
if (PermissionsUtil.hasManifestPermission(p_activity, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) { if (PermissionsUtil.hasManifestPermission(p_activity, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) {
WifiManager wifi = (WifiManager)p_activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE); WifiManager wifi = (WifiManager)p_activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
multicastLock = wifi.createMulticastLock("GodotMulticastLock"); multicastLock = wifi.createMulticastLock("GodotMulticastLock");

View File

@ -30,9 +30,8 @@
package org.godotengine.godot.utils; package org.godotengine.godot.utils;
import org.godotengine.godot.Godot;
import android.Manifest; import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo; import android.content.pm.PermissionInfo;
@ -65,7 +64,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method. * @param activity the caller activity for this method.
* @return true/false. "true" if permission was granted otherwise returns "false". * @return true/false. "true" if permission was granted otherwise returns "false".
*/ */
public static boolean requestPermission(String name, Godot activity) { public static boolean requestPermission(String name, Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Not necessary, asked on install already // Not necessary, asked on install already
return true; return true;
@ -93,7 +92,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method. * @param activity the caller activity for this method.
* @return true/false. "true" if all permissions were granted otherwise returns "false". * @return true/false. "true" if all permissions were granted otherwise returns "false".
*/ */
public static boolean requestManifestPermissions(Godot activity) { public static boolean requestManifestPermissions(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true; return true;
} }
@ -138,7 +137,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method. * @param activity the caller activity for this method.
* @return granted permissions list * @return granted permissions list
*/ */
public static String[] getGrantedPermissions(Godot activity) { public static String[] getGrantedPermissions(Activity activity) {
String[] manifestPermissions; String[] manifestPermissions;
try { try {
manifestPermissions = getManifestPermissions(activity); manifestPermissions = getManifestPermissions(activity);
@ -172,7 +171,7 @@ public final class PermissionsUtil {
* @param permission the permession to look for in the manifest file. * @param permission the permession to look for in the manifest file.
* @return "true" if the permission is in the manifest file of the activity, "false" otherwise. * @return "true" if the permission is in the manifest file of the activity, "false" otherwise.
*/ */
public static boolean hasManifestPermission(Godot activity, String permission) { public static boolean hasManifestPermission(Activity activity, String permission) {
try { try {
for (String p : getManifestPermissions(activity)) { for (String p : getManifestPermissions(activity)) {
if (permission.equals(p)) if (permission.equals(p))
@ -190,7 +189,7 @@ public final class PermissionsUtil {
* @return manifest permissions list * @return manifest permissions list
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/ */
private static String[] getManifestPermissions(Godot activity) throws PackageManager.NameNotFoundException { private static String[] getManifestPermissions(Activity activity) throws PackageManager.NameNotFoundException {
PackageManager packageManager = activity.getPackageManager(); PackageManager packageManager = activity.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
if (packageInfo.requestedPermissions == null) if (packageInfo.requestedPermissions == null)
@ -205,7 +204,7 @@ public final class PermissionsUtil {
* @return permission info object * @return permission info object
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/ */
private static PermissionInfo getPermissionInfo(Godot activity, String permission) throws PackageManager.NameNotFoundException { private static PermissionInfo getPermissionInfo(Activity activity, String permission) throws PackageManager.NameNotFoundException {
PackageManager packageManager = activity.getPackageManager(); PackageManager packageManager = activity.getPackageManager();
return packageManager.getPermissionInfo(permission, 0); return packageManager.getPermissionInfo(permission, 0);
} }

View File

@ -1150,7 +1150,7 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
JNIEnv *env = ThreadAndroid::get_env(); JNIEnv *env = ThreadAndroid::get_env();
jclass activityClass = env->FindClass("org/godotengine/godot/Godot"); jclass activityClass = env->FindClass("android/app/Activity");
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
classLoader = env->CallObjectMethod(p_activity, getClassLoader); classLoader = env->CallObjectMethod(p_activity, getClassLoader);
classLoader = (jclass)env->NewGlobalRef(classLoader); classLoader = (jclass)env->NewGlobalRef(classLoader);

View File

@ -77,14 +77,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
} }
} }
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject godot_instance, jobject p_asset_manager, jboolean p_use_apk_expansion) {
initialized = true; initialized = true;
JavaVM *jvm; JavaVM *jvm;
env->GetJavaVM(&jvm); env->GetJavaVM(&jvm);
// create our wrapper classes // create our wrapper classes
godot_java = new GodotJavaWrapper(env, activity); // our activity is our godot instance is our activity.. godot_java = new GodotJavaWrapper(env, activity, godot_instance);
godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env)); godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env));
ThreadAndroid::make_default(jvm); ThreadAndroid::make_default(jvm);
@ -109,7 +109,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
godot_java->on_video_init(env); godot_java->on_video_init(env);
} }
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity) { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
// lets cleanup // lets cleanup
if (godot_io_java) { if (godot_io_java) {
delete godot_io_java; delete godot_io_java;

View File

@ -37,8 +37,8 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code. // These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names) // See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" { extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject godot_instance, jobject p_asset_manager, jboolean p_use_apk_expansion);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits);

View File

@ -37,36 +37,47 @@
// TODO we could probably create a base class for this... // TODO we could probably create a base class for this...
GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance) {
godot_instance = p_env->NewGlobalRef(p_godot_instance); godot_instance = p_env->NewGlobalRef(p_godot_instance);
activity = p_env->NewGlobalRef(p_activity);
// get info about our Godot class so we can get pointers and stuff... // get info about our Godot class so we can get pointers and stuff...
cls = p_env->FindClass("org/godotengine/godot/Godot"); godot_class = p_env->FindClass("org/godotengine/godot/Godot");
if (cls) { if (godot_class) {
cls = (jclass)p_env->NewGlobalRef(cls); godot_class = (jclass)p_env->NewGlobalRef(godot_class);
} else {
// this is a pretty serious fail.. bail... pointers will stay 0
return;
}
activity_class = p_env->FindClass("android/app/Activity");
if (activity_class) {
activity_class = (jclass)p_env->NewGlobalRef(activity_class);
} else { } else {
// this is a pretty serious fail.. bail... pointers will stay 0 // this is a pretty serious fail.. bail... pointers will stay 0
return; return;
} }
// get some method pointers... // get some Godot method pointers...
_on_video_init = p_env->GetMethodID(cls, "onVideoInit", "()V"); _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
_restart = p_env->GetMethodID(cls, "restart", "()V"); _restart = p_env->GetMethodID(godot_class, "restart", "()V");
_finish = p_env->GetMethodID(cls, "forceQuit", "()V"); _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
_set_keep_screen_on = p_env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
_alert = p_env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
_get_GLES_version_code = p_env->GetMethodID(cls, "getGLESVersionCode", "()I"); _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
_get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
_request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); _request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
_request_permissions = p_env->GetMethodID(cls, "requestPermissions", "()Z"); _request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
_get_granted_permissions = p_env->GetMethodID(cls, "getGrantedPermissions", "()[Ljava/lang/String;"); _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
_init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V"); _init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V");
_get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;"); _get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;");
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z"); _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V"); _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;"); _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_main_loop_started = p_env->GetMethodID(cls, "onGodotMainLoopStarted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
} }
GodotJavaWrapper::~GodotJavaWrapper() { GodotJavaWrapper::~GodotJavaWrapper() {
@ -74,27 +85,25 @@ GodotJavaWrapper::~GodotJavaWrapper() {
} }
jobject GodotJavaWrapper::get_activity() { jobject GodotJavaWrapper::get_activity() {
// our godot instance is our activity return activity;
return godot_instance;
} }
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) { jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
if (cls) { if (godot_class) {
if (p_env == nullptr) if (p_env == nullptr)
p_env = ThreadAndroid::get_env(); p_env = ThreadAndroid::get_env();
jfieldID fid = p_env->GetStaticFieldID(cls, p_name, p_class); jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
return p_env->GetStaticObjectField(cls, fid); return p_env->GetStaticObjectField(godot_class, fid);
} else { } else {
return nullptr; return nullptr;
} }
} }
jobject GodotJavaWrapper::get_class_loader() { jobject GodotJavaWrapper::get_class_loader() {
if (cls) { if (_get_class_loader) {
JNIEnv *env = ThreadAndroid::get_env(); JNIEnv *env = ThreadAndroid::get_env();
jmethodID getClassLoader = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;"); return env->CallObjectMethod(godot_instance, _get_class_loader);
return env->CallObjectMethod(godot_instance, getClassLoader);
} else { } else {
return nullptr; return nullptr;
} }

View File

@ -43,7 +43,9 @@
class GodotJavaWrapper { class GodotJavaWrapper {
private: private:
jobject godot_instance; jobject godot_instance;
jclass cls; jobject activity;
jclass godot_class;
jclass activity_class;
jmethodID _on_video_init = 0; jmethodID _on_video_init = 0;
jmethodID _restart = 0; jmethodID _restart = 0;
@ -62,9 +64,10 @@ private:
jmethodID _vibrate = 0; jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0; jmethodID _get_input_fallback_mapping = 0;
jmethodID _on_godot_main_loop_started = 0; jmethodID _on_godot_main_loop_started = 0;
jmethodID _get_class_loader = 0;
public: public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
~GodotJavaWrapper(); ~GodotJavaWrapper();
jobject get_activity(); jobject get_activity();