Hello everybody,
I developped a plugin based on the awesome template from UE4 - Making a Android Plugin in 10 minutes - Isara Tech. in order to detect when my android device goes out from sleep mode. I integrated this plug-in into my UE4 project, compiled and packaged successfuly…
BUT when I run my application on android devices, it systematically crashes after splash screen exits… I am investigating and testing things for days now without finding any solution to this problem.
If someone has the skill or the knowledge to support me, I would be grateful and I can offer some french food gifts
Here is my xml file, where java code is encapsulated:
<?xml version="1.0" encoding="utf-8"?>
<!--GearVR plugin additions-->
<root xmlns:android="http://schemas.android.com/apk/res/android">
<!-- init section is always evaluated once per architecture -->
<trace enable="true"/>
<init>
<log text="AndroidAPITemplate init"/>
</init>
<androidManifestUpdates>
<addElements tag="application">
<activity android:name="com.isaratech.androidapi.AndroidAPITemplateActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name" />
</addElements>
</androidManifestUpdates>
<!-- optional additions to proguard -->
<proguardAdditions>
<insert><![CDATA[
-keepattributes Signature
-dontskipnonpubliclibraryclassmembers
-keepclassmembers class com.epicgames.ue4.GameActivity {
public <methods>;
public <fields>;
}
]]></insert>
</proguardAdditions>
<resourceCopies>
<!-- Copy the generated resource file to be packaged -->
</resourceCopies>
<AARImports>
</AARImports>
<!-- optional additions to the GameActivity imports in GameActivity.java -->
<gameActivityImportAdditions>
<insert>
import java.util.HashSet;
import java.util.Arrays;
import android.text.TextUtils;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.Toast;
import android.content.BroadcastReceiver;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
</insert>
</gameActivityImportAdditions>
<!-- optional additions to the GameActivity class in GameActivity.java -->
<gameActivityClassAdditions>
<insert>
<![CDATA[
private Boolean WakeUpFlag = false;
/**
* returns Current value of the WakeUpFlag
*/
public void AndroidThunkJava_AndroidAPI_GetWakeUpFlagValue(Boolean WakeUpFlag_b) {
WakeUpFlag_b = WakeUpFlag;
}
]]>
</insert>
</gameActivityClassAdditions>
<!-- optional additions to GameActivity onCreate metadata reading in GameActivity.java -->
<gameActivityReadMetadataAdditions>
<insert>
</insert>
</gameActivityReadMetadataAdditions>
<!-- optional additions to GameActivity onCreate in GameActivity.java -->
<gameActivityOnCreateAdditions>
<insert>
<![CDATA[
Context context = getApplicationContext();
final PowerManager mPowermanager = (PowerManager) context.getSystemService((Context.POWER_SERVICE));
IntentFilter filter = new IntentFilter();
filter.addAction((Intent.ACTION_SCREEN_ON));
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
WakeUpFlag = mPowermanager.isInteractive();
}
},filter);
]]>
</insert>
</gameActivityOnCreateAdditions>
<!-- optional additions to GameActivity onDestroy in GameActivity.java -->
<gameActivityOnDestroyAdditions>
<insert>
</insert>
</gameActivityOnDestroyAdditions>
<!-- optional additions to GameActivity onStart in GameActivity.java -->
<gameActivityOnStartAdditions>
<insert>
</insert>
</gameActivityOnStartAdditions>
<!-- optional additions to GameActivity onStop in GameActivity.java -->
<gameActivityOnStopAdditions>
<insert>
</insert>
</gameActivityOnStopAdditions>
<!-- optional additions to GameActivity onPause in GameActivity.java -->
<gameActivityOnPauseAdditions>
<insert>
<![CDATA[
]]>
</insert>
</gameActivityOnPauseAdditions>
<!-- optional additions to GameActivity onResume in GameActivity.java -->
<gameActivityOnResumeAdditions>
<insert>
</insert>
</gameActivityOnResumeAdditions>
<!-- optional additions to GameActivity onActivityResult in GameActivity.java -->
<gameActivityOnActivityResultAdditions>
<insert>
</insert>
</gameActivityOnActivityResultAdditions>
<!-- optional libraries to load in GameActivity.java before libUE4.so -->
<soLoadLibrary>
<!-- need this if plugin enabled and supported architecture, even if not packaged for GearVR -->
</soLoadLibrary>
</root>
Here is my cpp plugin code:
#include "AndroidAPITemplatePrivatePCH.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "AndroidApplication.h"
#define INIT_JAVA_METHOD(name, signature) \
if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true)) { \
name = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, #name, signature, false); \
check(name != NULL); \
} else { \
check(0); \
}
#define DECLARE_JAVA_METHOD(name) \
static jmethodID name = NULL;
DECLARE_JAVA_METHOD(AndroidThunkJava_AndroidAPI_ShowToast); // Here goes the name of the method in the Java side
DECLARE_JAVA_METHOD(AndroidThunkJava_AndroidAPI_GetWakeUpFlagValue);
void UAndroidAPITemplateFunctions::InitJavaFunctions()
{
// Same here, but we add the Java signature (the type of the parameters is between the parameters, and the return type is added at the end,
// here the return type is V for "void")
// More details here about Java signatures: http://www.rgagnon.com/javadetails/java-0286.html
INIT_JAVA_METHOD(AndroidThunkJava_AndroidAPI_GetWakeUpFlagValue, "(Z)V ");
}
#undef DECLARE_JAVA_METHOD
#undef INIT_JAVA_METHOD
#endif
bool UAndroidAPITemplateFunctions::AndroidAPITemplate_GetWakeUpFlagValue()
{
#if PLATFORM_ANDROID
if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true))
{
jboolean Result = false;
FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, AndroidThunkJava_AndroidAPI_GetWakeUpFlagValue, Result);
/*const bool ReturnResult_b = Result;
Env->DeleteLocalRef(Result);*/
return Result;
}
else
{
UE_LOG(LogAndroid, Warning, TEXT("ERROR: Could not get Java ENV\n"));
return false;
}
#else
return false;
#endif
}
Here is my plugin header:
#pragma once
#include "AndroidAPITemplateFunctions.generated.h"
UCLASS(NotBlueprintable)
class UAndroidAPITemplateFunctions : public UObject {
GENERATED_BODY()
public:
#if PLATFORM_ANDROID
static void InitJavaFunctions();
#endif
/**
* Get the WakeUpFlag value
*/
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (Keywords = "AndroidAPI ", DisplayName = "Get Wake Up Flag Value"), Category = "AndroidAPI")
static bool AndroidAPITemplate_GetWakeUpFlagValue();
};
and finally the build.cs for my plugin:
using System.IO;
namespace UnrealBuildTool.Rules
{
public class AndroidAPITemplate : ModuleRules
{
public AndroidAPITemplate(ReadOnlyTargetRules Target) : base(Target)
{
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
"Developer/AndroidAPITemplate/Private",
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
PrivateIncludePathModuleNames.AddRange(
new string[] {
"Settings",
"Launch",
}
);
if (Target.Platform == UnrealTargetPlatform.IOS) {
}
else if(Target.Platform == UnrealTargetPlatform.Android)
{
string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(PluginPath, "AndroidAPITemplate_APL.xml")));
}
}
}
}