Base Android implementation

This commit is contained in:
Karim Akra
2025-10-09 18:29:16 +08:00
committed by Hundrec
parent 28883fb9ed
commit 1b0a0a476d
4 changed files with 176 additions and 4 deletions
+62 -3
View File
@@ -113,7 +113,10 @@ class Project extends HXProject
*/
static final ANDROID_TARGET_SDK_VERSION:Int = 35;
static final ANDROID_EXTENSIONS:Array<String> = ["funkin.extensions.CallbackUtil"];
static final ANDROID_EXTENSIONS:Array<String> = [
"funkin.extensions.CallbackUtil",
"funkin.extensions.FNFCExtension"
];
/**
* The team ID to use for the iOS app. Configured in XCode.
@@ -1088,6 +1091,7 @@ class Project extends HXProject
}
}
// Custom android.application XML Children for the AndroidManifest
final modsFolderProvider:Xml = Xml.createElement('provider');
modsFolderProvider.set('android:authorities', '$PACKAGE_NAME.docprovider');
modsFolderProvider.set('android:name', 'funkin.provider.DataFolderProvider');
@@ -1107,14 +1111,69 @@ class Project extends HXProject
{
if (config.get('android.application').xmlChildren != null)
{
config.get('android.application').xmlChildren.push(Printer.print(modsFolderProvider));
config.get('android.application').xmlChildren.push(Printer.print(modsFolderProvider, true));
}
else
{
config.get('android.application').xmlChildren = [Printer.print(modsFolderProvider)];
config.get('android.application').xmlChildren = [Printer.print(modsFolderProvider, true)];
}
}
// Custom android.activity XML Children for the AndroidManifest
final intentFilter:Xml = Xml.createElement('intent-filter');
final action:Xml = Xml.createElement('action');
action.set('android:name', 'android.intent.action.VIEW');
intentFilter.addChild(action);
final intentFilterCategories:Array<String> = [
"android.intent.category.DEFAULT",
"android.intent.category.BROWSABLE"
];
for (categoryName in intentFilterCategories)
{
final category:Xml = Xml.createElement('category');
category.set('android:name', categoryName);
intentFilter.addChild(category);
}
final intentFilterDatas:Map<String, Array<String>> = [
"android:mimeType" => ["application/zip"],
// When this is added, it makes the game show up only when it's a FNFC file.
// But it also for some reason make it not show up at all in ZArchiver no matter what the file is...
// "android:host" => ["*"],
"android:scheme" => ["file", "content"],
// I think having all these makes it so the extension check would work even when the files has dots like "epic-song-v2.0.fnfc"
"android:pathPattern" => [
".*\\.fnfc", ".*\\..*\\.fnfc",
".*\\..*\\..*\\.fnfc",
".*\\..*\\..*\\..*\\.fnfc",
".*\\..*\\..*\\..*\\..*\\.fnfc"
]
];
for (key => values in intentFilterDatas) {
for (value in values) {
final data:Xml = Xml.createElement("data");
data.set(key, value);
intentFilter.addChild(data);
}
}
@:nullSafety(Off)
{
if (config.get('android.activity').xmlChildren != null)
{
config.get('android.activity').xmlChildren.push(Printer.print(intentFilter, true));
}
else
{
config.get('android.activity').xmlChildren = [Printer.print(intentFilter, true)];
}
}
final extensions:Null<Array<String>> = config.getArrayString('android.extension');
for (extension in ANDROID_EXTENSIONS)
+21
View File
@@ -30,6 +30,13 @@ class CallbackUtil
*/
public static var onActivityResult:FlxTypedSignal<Int->Int->Void> = new FlxTypedSignal<Int->Int->Void>();
/**
* Signal triggered when the user opens a FNFC file with the game in runtime.
*
* First argument is the FNFC file path.
*/
public static var onOpenFNFC:FlxTypedSignal<String->Void> = new FlxTypedSignal<String->Void>();
/**
* Initializes the callback utility.
*/
@@ -66,5 +73,19 @@ class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
{
if (CallbackUtil.onActivityResult != null) CallbackUtil.onActivityResult.dispatch(requestCode, resultCode);
}
/**
* Handles the onOpenFNFC callback from native code.
*
* @param file Path to the FNFC file that was opened.
*/
@:keep
#if (lime >= "8.0.0")
@:runOnMainThread
#end
public function onOpenFNFC(file:String):Void
{
if (CallbackUtil.onOpenFNFC != null) CallbackUtil.onOpenFNFC.dispatch(file);
}
}
#end
@@ -25,10 +25,22 @@ public class CallbackUtil extends Extension
CallbackUtil.haxeObject = haxeObject;
}
public static void callMethod(String methodName, Object[] args)
{
if (haxeObject != null)
haxeObject.call(methodName, args);
}
public static void callMethodVoid(String methodName)
{
if (haxeObject != null)
haxeObject.call0(methodName);
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
{
if (haxeObject != null)
if (haxeObject != null)
haxeObject.call2("onActivityResult", requestCode, resultCode);
return true;
@@ -0,0 +1,80 @@
package funkin.extensions;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import funkin.extensions.CallbackUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.haxe.extension.Extension;
import org.haxe.lime.HaxeObject;
public class FNFCExtension extends Extension
{
public static String launchedFile = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = mainActivity.getIntent();
if (intent != null && intent.getData() != null) {
handleIntent(intent, false);
}
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null && intent.getData() != null) {
handleIntent(mainActivity.getIntent(), true);
}
}
public static String queryLaunchedFNFC()
{
return launchedFile;
}
private static void handleIntent(Intent intent, boolean doCallback)
{
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri != null) {
File output = new File(mainContext.getCacheDir(), new File("fnfc", uri.getPath()).getName());
try {
InputStream in = mainContext.getContentResolver().openInputStream(uri);
OutputStream out = new FileOutputStream(output);
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
catch (Exception e) {
Log.e("Exception", e.getMessage());
}
launchedFile = output.getAbsolutePath();
if (doCallback)
{
Object[] args = new Object[1];
args[0] = launchedFile;
CallbackUtil.callMethod("onOpenFNFC", args);
}
}
}
}
}