From 9e04db2c5c9ee74a2d1f1c2570e0bfedd361b403 Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Wed, 9 Jul 2025 12:02:55 +0300 Subject: [PATCH] Adjustments --- project.hxp | 112 ++++--- source/Main.hx | 9 +- source/funkin/InitState.hx | 2 +- .../mobile/external/android/ScreenUtils.hx | 76 +++++ .../funkin/provider/DataFolderProvider.java | 302 ++++++++++++++++++ .../android/java/funkin/util/ScreenUtils.java | 36 +++ .../mobile/external/{ => ios}/AudioSession.hx | 2 +- .../mobile/external/{ => ios}/ScreenUtils.hx | 3 +- .../funkin/mobile/external/project/Build.xml | 29 -- .../project/ios/include/AudioSession.hpp | 4 - .../project/ios/include/ScreenUtils.hpp | 4 - .../external/project/ios/src/AudioSession.mm | 41 --- .../external/project/ios/src/ScreenUtils.mm | 36 --- source/funkin/mobile/util/ScreenUtil.hx | 6 +- source/funkin/util/CLIUtil.hx | 2 +- .../java/me/funkin/fnf/ModFolderProvider.java | 299 ----------------- 16 files changed, 481 insertions(+), 482 deletions(-) create mode 100644 source/funkin/mobile/external/android/ScreenUtils.hx create mode 100644 source/funkin/mobile/external/android/java/funkin/provider/DataFolderProvider.java create mode 100644 source/funkin/mobile/external/android/java/funkin/util/ScreenUtils.java rename source/funkin/mobile/external/{ => ios}/AudioSession.hx (90%) rename source/funkin/mobile/external/{ => ios}/ScreenUtils.hx (93%) delete mode 100644 source/funkin/mobile/external/project/Build.xml delete mode 100644 source/funkin/mobile/external/project/ios/include/AudioSession.hpp delete mode 100644 source/funkin/mobile/external/project/ios/include/ScreenUtils.hpp delete mode 100644 source/funkin/mobile/external/project/ios/src/AudioSession.mm delete mode 100644 source/funkin/mobile/external/project/ios/src/ScreenUtils.mm delete mode 100644 templates/android/template/app/src/main/java/me/funkin/fnf/ModFolderProvider.java diff --git a/project.hxp b/project.hxp index 4e4d68f58..1767594b0 100644 --- a/project.hxp +++ b/project.hxp @@ -488,6 +488,7 @@ class Project extends HXProject configureApp(); displayTarget(); + configureFeatureFlags(); configureCompileDefines(); configureIncludeMacros(); @@ -497,8 +498,6 @@ class Project extends HXProject configureHaxelibs(); configureAssets(); configureIcons(); - if (isAndroid()) - configureDocumentProvider(); readASTCExclusion(); @@ -512,23 +511,14 @@ class Project extends HXProject configureAdMobKeys(); } - // To sign in ios, use -xcode. if (isAndroid()) { - if (isRelease()) - { - configureAndroidRelease(); - } + configureAndroid(); } if (isIOS()) { - config.set("ios.non-exempt-encryption", "false"); - config.set("ios.team-id", IOS_TEAM_ID); - config.set("ios.deployment", "16.0"); - - // launchStoryboard = new LaunchStoryboard(); - // launchStoryboard.path = "./LaunchScreen.storyboard"; + configureIOS(); } info("Done configuring project."); @@ -979,38 +969,72 @@ class Project extends HXProject app.path = buildDir; } - function configureAndroidRelease() + function configureAndroid() { - if (envConfig != null) + javaPaths.push(Path.join([SOURCE_DIR, 'funkin/mobile/external/android/java'])); + + if (isRelease()) { - keystore = new Keystore(); - keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH')); - keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS')); - keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD')); + if (envConfig != null) + { + keystore = new Keystore(); + keystore.path = Std.string(envConfig.get('ANDROID_KEYSTORE_PATH')); + keystore.alias = Std.string(envConfig.get('ANDROID_KEYSTORE_ALIAS')); + keystore.password = Std.string(envConfig.get('ANDROID_KEYSTORE_PASSWORD')); + } + + Reflect.setField(config.get('android.manifest'), 'xmlns:tools', 'http://schemas.android.com/tools'); + + final permissionsConfig:Array = [ + '', + '' + ]; + + final xmlChildren:Null> = config.get('android.manifest').xmlChildren; + + if (xmlChildren != null) + { + config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig); + } + else + { + config.get('android.manifest').xmlChildren = permissionsConfig; + } + + if (isBundle()) + { + config.set("android.gradle-project-directory", "bin-bundle"); + } } - Reflect.setField(config.get('android.manifest'), 'xmlns:tools', 'http://schemas.android.com/tools'); - - final permissionsConfig:Array = [ - '', - '' + final modsFolderProvider = [ + '', + ' ', + ' ', + ' ', + '' ]; - final xmlChildren:Null> = config.get('android.manifest').xmlChildren; + final xmlChildren:Null> = config.get('android.application').xmlChildren; if (xmlChildren != null) { - config.get('android.manifest').xmlChildren = xmlChildren.concat(permissionsConfig); + config.get('android.application').xmlChildren = xmlChildren.concat(modsFolderProvider); } else { - config.get('android.manifest').xmlChildren = permissionsConfig; + config.get('android.application').xmlChildren = modsFolderProvider; } + } - if (isBundle()) - { - config.set("android.gradle-project-directory", "bin-bundle"); - } + function configureIOS() + { + config.set("ios.non-exempt-encryption", "false"); + config.set("ios.team-id", IOS_TEAM_ID); + config.set("ios.deployment", "16.0"); + + // launchStoryboard = new LaunchStoryboard(); + // launchStoryboard.path = "./LaunchScreen.storyboard"; } function configurePolymod() @@ -1261,32 +1285,6 @@ class Project extends HXProject } } - function configureDocumentProvider() - { - final providerXml = [' - - - - - ']; - - final xmlChildren:Null> = config.get('android.application').xmlChildren; - - if (xmlChildren != null) - { - config.get('android.application').xmlChildren = xmlChildren.concat(providerXml); - } - else - { - config.get('android.application').xmlChildren = providerXml; - } - } - /** * Configure the platforms' Ad Mob application keys. */ diff --git a/source/Main.hx b/source/Main.hx index 8d74a7154..e48765854 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -38,12 +38,11 @@ class Main extends Sprite { // Set the current working directory for Android and iOS devices #if android - // For Android we determine the appropriate directory based on Android version - Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.os.Build.VERSION.SDK_INT > 30 ? extension.androidtools.content.Context.getObbDir() : // Use Obb directory for Android SDK version > 30 - extension.androidtools.content.Context.getExternalFilesDir() // Use External Files directory for Android SDK version < 30 - )); + // On Android use External Files Dir. + Sys.setCwd(haxe.io.Path.addTrailingSlash(extension.androidtools.content.Context.getExternalFilesDir())); #elseif ios - Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory)); // For iOS we use documents directory and this is only way we can do. + // On iOS use Documents Dir. + Sys.setCwd(haxe.io.Path.addTrailingSlash(lime.system.System.documentsDirectory)); #end // We need to make the crash handler LITERALLY FIRST so nothing EVER gets past it. diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 0258ed3db..6e9535b16 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -114,7 +114,7 @@ class InitState extends FlxState #if ios // Setup Audio session - funkin.mobile.external.AudioSession.initialize(); + funkin.mobile.external.ios.AudioSession.initialize(); #end // This ain't a pixel art game! (most of the time) diff --git a/source/funkin/mobile/external/android/ScreenUtils.hx b/source/funkin/mobile/external/android/ScreenUtils.hx new file mode 100644 index 000000000..d58225c4e --- /dev/null +++ b/source/funkin/mobile/external/android/ScreenUtils.hx @@ -0,0 +1,76 @@ +package funkin.mobile.external.android; + +import lime.math.Rectangle; +import lime.system.JNI; + +/** + * A Utility class to get Android screen related informations. + */ +class ScreenUtils +{ + @:noCompletion + private static var staticMethodCache:Map = []; + + @:noCompletion + private static var memberFieldCache:Map = []; + + public static function getCutoutDimensions():Array + { + final getCutoutDimensionsJNI:Null = createStaticMethod('funkin/util/ScreenUtils', 'getCutoutDimensions', '()[Landroid/graphics/Rect;'); + + if (getCutoutDimensionsJNI != null) + { + final rectangles:Array = []; + + for (rectangle in cast(getCutoutDimensionsJNI(), Array)) + { + if (rectangle == null) continue; + + final topJNI:Null = createMemberField('android/graphics/Rect', 'top', 'I'); + final leftJNI:Null = createMemberField('android/graphics/Rect', 'left', 'I'); + final rightJNI:Null = createMemberField('android/graphics/Rect', 'right', 'I'); + final bottomJNI:Null = createMemberField('android/graphics/Rect', 'bottom', 'I'); + + if (topJNI != null && leftJNI != null && rightJNI != null && bottomJNI != null) + { + final top:Int = topJNI.get(rectangle); + final left:Int = leftJNI.get(rectangle); + final right:Int = rightJNI.get(rectangle); + final bottom:Int = bottomJNI.get(rectangle); + + rectangles.push(new Rectangle(left, top, right - left, bottom - top)); + } + } + + return rectangles; + } + + return []; + } + + public static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null + { + @:privateAccess + className = JNI.transformClassName(className); + + final key:String = '$className::$methodName::$signature'; + + if (cache && !staticMethodCache.exists(key)) staticMethodCache.set(key, JNI.createStaticMethod(className, methodName, signature)); + else if (!cache) return JNI.createStaticMethod(className, methodName, signature); + + return staticMethodCache.get(key); + } + + public static function createMemberField(className:String, fieldName:String, signature:String, cache:Bool = true):Null + { + @:privateAccess + className = JNI.transformClassName(className); + + final key:String = '$className::$fieldName::$signature'; + + if (cache && !memberFieldCache.exists(key)) memberFieldCache.set(key, JNI.createMemberField(className, fieldName, signature)); + else if (!cache) return JNI.createMemberField(className, fieldName, signature); + + return memberFieldCache.get(key); + } +} diff --git a/source/funkin/mobile/external/android/java/funkin/provider/DataFolderProvider.java b/source/funkin/mobile/external/android/java/funkin/provider/DataFolderProvider.java new file mode 100644 index 000000000..7d3d791d1 --- /dev/null +++ b/source/funkin/mobile/external/android/java/funkin/provider/DataFolderProvider.java @@ -0,0 +1,302 @@ +package funkin.provider; + +import android.annotation.SuppressLint; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.Point; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.provider.DocumentsProvider; +import android.provider.DocumentsContract.Root; +import android.provider.DocumentsContract.Document; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; + +public class DataFolderProvider extends DocumentsProvider +{ + private static final String TAG = "DataFolderProvider"; + + private static final String ROOT = "root"; + + private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES}; + + private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE}; + + private File rootDir; + + @Override + public Cursor queryRoots(String[] projection) throws FileNotFoundException + { + final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION); + + final MatrixCursor.RowBuilder row = result.newRow(); + + row.add(Root.COLUMN_ROOT_ID, ROOT); + + row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD); + + row.add(Root.COLUMN_SUMMARY, "Data Folder"); + + row.add(Root.COLUMN_DOCUMENT_ID, getDocumentID(rootDir)); + + ::if (APP_TITLE != "")::row.add(Root.COLUMN_TITLE, "::APP_TITLE::");::end:: + + ::if (APP_PACKAGE != "")::row.add(Root.COLUMN_ICON, ::APP_PACKAGE::.R.mipmap.ic_launcher);::end:: + + return result; + } + + @Override + public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException + { + final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + + includeFile(result, getFileFromDocumentID(documentId)); + + return result; + } + + @Override + public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException + { + final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + + for (File file : getFileFromDocumentID(parentDocumentId).listFiles()) + { + includeFile(result, file); + } + + return result; + } + + @Override + public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException + { + return ParcelFileDescriptor.open(getFileFromDocumentID(documentId), ParcelFileDescriptor.parseMode(mode)); + } + + @SuppressLint({"SetWorldReadable", "SetWorldWritable"}) + @Override + public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException + { + File parentPath = getFileFromDocumentID(parentDocumentId); + + try + { + File file = new File(parentPath, displayName); + + if (mimeType.equals(Document.MIME_TYPE_DIR)) + assert file.mkdir(); + else + assert file.createNewFile(); + + assert file.setReadable(true, false); + assert file.setWritable(true, false); + + return getDocumentID(file); + } + catch (IOException e) + { + throw new FileNotFoundException("Cannot create file with id " + parentDocumentId + "/" + displayName); + } + } + + @Override + public void deleteDocument(String documentId) throws FileNotFoundException + { + File file = getFileFromDocumentID(documentId); + + Boolean result = false; + + try + { + if (file.isDirectory()) + { + result = deleteFolderRecursively(file); + } + else + { + result = file.delete(); + } + } + catch (Exception e) + { + Log.e(TAG, e.toString()); + + e.printStackTrace(); + } + + if (!result) + { + throw new FileNotFoundException("Failed to delete document with id " + documentId); + } + } + + public boolean deleteFolderRecursively(File folder) + { + boolean isSuccessful = true; + + final File[] files = folder.listFiles(); + + if (files == null || !folder.exists()) + return false; + + for (File item : files) + { + if (item.isDirectory()) + { + isSuccessful = isSuccessful && deleteFolderRecursively(item); + } + else + { + isSuccessful = isSuccessful && item.delete(); + } + } + + isSuccessful = isSuccessful && folder.delete(); + + return isSuccessful; + } + + @Override + public String renameDocument(String documentId, String displayName) throws FileNotFoundException + { + File origin = getFileFromDocumentID(documentId); + File dest = new File(origin.getParentFile(), displayName); + + if (!origin.renameTo(dest)) + { + throw new FileNotFoundException("Failed to rename document with id " + documentId + " to " + displayName); + } + + return getDocumentID(dest); + } + + @Override + public boolean isChildDocument(String parentDocumentId, String documentId) + { + return documentId.startsWith(parentDocumentId); + } + + @Override + public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException + { + final File file = getFileFromDocumentID(documentId); + final ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + return new AssetFileDescriptor(parcelFileDescriptor, 0, AssetFileDescriptor.UNKNOWN_LENGTH); + } + + private String getDocumentID(File file) + { + Path filePath = file.toPath().normalize(); + Path rootPath = rootDir.toPath(); + + if (!filePath.startsWith(rootPath)) + return null; + + return ROOT + ":" + rootPath.relativize(filePath).toString(); + } + + private File getFileFromDocumentID(String id) + { + final String[] separatedPath = id.split(":", 2); + + if (separatedPath.length < 2) + return null; + + if (!separatedPath[0].equals(ROOT)) + return null; + + final String filePath = separatedPath[1]; + + if (filePath.isEmpty()) + return rootDir; + + final Path rootPath = rootDir.toPath().normalize().toAbsolutePath(); + final Path desiredPath = rootPath.resolve(filePath).normalize().toAbsolutePath(); + + if (!desiredPath.startsWith(rootPath)) + { + return null; + } + + return desiredPath.toFile(); + } + + private void includeFile(MatrixCursor result, File file) + { + if (file == null) + file = rootDir; + + int flags = 0; + final String mimeType = getTypeForFile(file); + + if (file.canWrite()) + { + flags |= Document.FLAG_SUPPORTS_WRITE | + Document.FLAG_SUPPORTS_DELETE | + Document.FLAG_SUPPORTS_RENAME; + + if (file.isDirectory()) + flags |= Document.FLAG_DIR_SUPPORTS_CREATE; + } + + if (mimeType.startsWith("image/")) + { + flags |= Document.FLAG_SUPPORTS_THUMBNAIL; + } + + final MatrixCursor.RowBuilder row = result.newRow(); + row.add(Document.COLUMN_DOCUMENT_ID, getDocumentID(file)); + row.add(Document.COLUMN_DISPLAY_NAME, file.getName()); + row.add(Document.COLUMN_SIZE, file.length()); + row.add(Document.COLUMN_MIME_TYPE, mimeType); + row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified()); + row.add(Document.COLUMN_FLAGS, flags); + } + + private static String getTypeForFile(File file) + { + if (file.isDirectory()) + { + return Document.MIME_TYPE_DIR; + } + else + { + return getTypeForName(file.getName()); + } + } + + private static String getTypeForName(String name) + { + final int lastDot = name.lastIndexOf('.'); + if (lastDot >= 0) + { + final String extension = name.substring(lastDot + 1); + + if (extension.equals("hxc") || extension.equals("hx")) + return "application/*"; + + final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mime != null) + { + return mime; + } + } + return "application/octet-stream"; + } + + @Override + public boolean onCreate() + { + rootDir = getContext().getExternalFilesDir(null); + + return true; + } +} diff --git a/source/funkin/mobile/external/android/java/funkin/util/ScreenUtils.java b/source/funkin/mobile/external/android/java/funkin/util/ScreenUtils.java new file mode 100644 index 000000000..a4c9630fa --- /dev/null +++ b/source/funkin/mobile/external/android/java/funkin/util/ScreenUtils.java @@ -0,0 +1,36 @@ +package funkin.util; + +import android.graphics.Rect; +import android.os.Build; +import android.view.DisplayCutout; +import android.view.WindowInsets; + +import java.util.List; + +import org.haxe.extension.Extension; + +public class ScreenUtils +{ + public static Rect[] getCutoutDimensions() + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) + { + WindowInsets insets = Extension.mainActivity.getWindow().getDecorView().getRootWindowInsets(); + + if (insets != null) + { + DisplayCutout cutout = insets.getDisplayCutout(); + + if (cutout != null) + { + List boundingRects = cutout.getBoundingRects(); + + if (boundingRects != null && !boundingRects.isEmpty()) + return boundingRects.toArray(new Rect[0]); + } + } + } + + return new Rect[0]; + } +} diff --git a/source/funkin/mobile/external/AudioSession.hx b/source/funkin/mobile/external/ios/AudioSession.hx similarity index 90% rename from source/funkin/mobile/external/AudioSession.hx rename to source/funkin/mobile/external/ios/AudioSession.hx index 986cb2445..deca7e3ce 100644 --- a/source/funkin/mobile/external/AudioSession.hx +++ b/source/funkin/mobile/external/ios/AudioSession.hx @@ -1,4 +1,4 @@ -package funkin.mobile.external; +package funkin.mobile.external.ios; /** * A Utility class to manage iOS audio. diff --git a/source/funkin/mobile/external/ScreenUtils.hx b/source/funkin/mobile/external/ios/ScreenUtils.hx similarity index 93% rename from source/funkin/mobile/external/ScreenUtils.hx rename to source/funkin/mobile/external/ios/ScreenUtils.hx index cf262b178..c1e532dea 100644 --- a/source/funkin/mobile/external/ScreenUtils.hx +++ b/source/funkin/mobile/external/ios/ScreenUtils.hx @@ -1,4 +1,4 @@ -package funkin.mobile.external; +package funkin.mobile.external.ios; /** * A Utility class to get iOS screen related informations. @@ -10,6 +10,7 @@ extern class ScreenUtils { @:native('getSafeAreaInsets') static function getSafeAreaInsets(top:cpp.RawPointer, bottom:cpp.RawPointer, left:cpp.RawPointer, right:cpp.RawPointer):Void; + @:native('getScreenSize') static function getScreenSize(width:cpp.RawPointer, height:cpp.RawPointer):Void; } diff --git a/source/funkin/mobile/external/project/Build.xml b/source/funkin/mobile/external/project/Build.xml deleted file mode 100644 index 22d80effb..000000000 --- a/source/funkin/mobile/external/project/Build.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- - - -
- - -
-
diff --git a/source/funkin/mobile/external/project/ios/include/AudioSession.hpp b/source/funkin/mobile/external/project/ios/include/AudioSession.hpp deleted file mode 100644 index f0e752dcf..000000000 --- a/source/funkin/mobile/external/project/ios/include/AudioSession.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void initialize(); -void setActive(bool active); diff --git a/source/funkin/mobile/external/project/ios/include/ScreenUtils.hpp b/source/funkin/mobile/external/project/ios/include/ScreenUtils.hpp deleted file mode 100644 index b6036d8b8..000000000 --- a/source/funkin/mobile/external/project/ios/include/ScreenUtils.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void getSafeAreaInsets(double* top, double* bottom, double* left, double* right); -void getScreenSize(double* width, double* height); diff --git a/source/funkin/mobile/external/project/ios/src/AudioSession.mm b/source/funkin/mobile/external/project/ios/src/AudioSession.mm deleted file mode 100644 index db9569366..000000000 --- a/source/funkin/mobile/external/project/ios/src/AudioSession.mm +++ /dev/null @@ -1,41 +0,0 @@ -#import -#import - -void initialize() -{ - AVAudioSession *session = [AVAudioSession sharedInstance]; - - NSError *error; - [session setCategory:AVAudioSessionCategoryPlayback - mode:AVAudioSessionModeDefault - options:AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers - error:&error]; - - if (@available(iOS 17.0, *)) - { - [session setPrefersInterruptionOnRouteDisconnect:false error:nil]; - } - - if (@available(iOS 14.5, *)) - { - [session setPrefersNoInterruptionsFromSystemAlerts:true error:nil]; - } - - if (error) - { - NSLog(@"Unable to set category of audio session: %@", error); - } -} - -void setActive(bool active) -{ - AVAudioSession *session = [AVAudioSession sharedInstance]; - NSError *error; - - [session setActive:YES error:&error]; - - if (error) - { - NSLog(@"Unable to set active of audio session: %@", error); - } -} diff --git a/source/funkin/mobile/external/project/ios/src/ScreenUtils.mm b/source/funkin/mobile/external/project/ios/src/ScreenUtils.mm deleted file mode 100644 index 77fb6ed9b..000000000 --- a/source/funkin/mobile/external/project/ios/src/ScreenUtils.mm +++ /dev/null @@ -1,36 +0,0 @@ -#import "ScreenUtils.hpp" - -#import -#import - -void getSafeAreaInsets(double* top, double* bottom, double* left, double* right) -{ - if (@available(iOS 11, *)) - { - UIWindow* window = [UIApplication sharedApplication].windows[0]; - UIEdgeInsets safeAreaInsets = window.safeAreaInsets; - - float scale = [UIScreen mainScreen].scale; - - (*top) = safeAreaInsets.top * scale; - (*bottom) = safeAreaInsets.bottom * scale; - (*left) = safeAreaInsets.left * scale; - (*right) = safeAreaInsets.right * scale; - - return; - } - - (*top) = 0.0; - (*bottom) = 0.0; - (*left) = 0.0; - (*right) = 0.0; -} - -void getScreenSize(double* width, double* height) -{ - CGRect screenRect = [[UIScreen mainScreen] bounds]; - float scale = [UIScreen mainScreen].scale; - - (*width) = (double)screenRect.size.width * scale; - (*height) = (double)screenRect.size.height * scale; -} diff --git a/source/funkin/mobile/util/ScreenUtil.hx b/source/funkin/mobile/util/ScreenUtil.hx index bb123003d..72b2209de 100644 --- a/source/funkin/mobile/util/ScreenUtil.hx +++ b/source/funkin/mobile/util/ScreenUtil.hx @@ -1,9 +1,9 @@ package funkin.mobile.util; #if ios -import funkin.mobile.external.ScreenUtils; +import funkin.mobile.external.ios.ScreenUtils; #elseif android -import extension.androidtools.Tools; +import funkin.mobile.external.android.ScreenUtils; #end import lime.math.Rectangle; import lime.system.System; @@ -26,7 +26,7 @@ class ScreenUtil final rectDimensions:Array> = [[], [], [], []]; // Push all the dimensions of the cutouts into an array - for (rect in Tools.getCutoutDimensions()) + for (rect in ScreenUtils.getCutoutDimensions()) { rectDimensions[0].push(rect.x); rectDimensions[1].push(rect.y); diff --git a/source/funkin/util/CLIUtil.hx b/source/funkin/util/CLIUtil.hx index 2b2db3088..64e0e4257 100644 --- a/source/funkin/util/CLIUtil.hx +++ b/source/funkin/util/CLIUtil.hx @@ -18,7 +18,7 @@ class CLIUtil var cwd:String = Path.addTrailingSlash(Sys.getCwd()); var gameDir:String = ''; #if android - gameDir = Path.addTrailingSlash(extension.androidtools.os.Build.VERSION.SDK_INT > 30 ? extension.androidtools.content.Context.getObbDir() : extension.androidtools.content.Context.getExternalFilesDir()); + gameDir = Path.addTrailingSlash(extension.androidtools.content.Context.getExternalFilesDir()); #elseif ios // Why? Because for some reason lime.system.System.documentsDirectory is returning a directory that's different and we're unable to read or write from, so it's disabled and no solution is found... trace('[WARN]: Reseting the Current Working Directory is unavailable on iOS targets'); diff --git a/templates/android/template/app/src/main/java/me/funkin/fnf/ModFolderProvider.java b/templates/android/template/app/src/main/java/me/funkin/fnf/ModFolderProvider.java deleted file mode 100644 index 975d7e39b..000000000 --- a/templates/android/template/app/src/main/java/me/funkin/fnf/ModFolderProvider.java +++ /dev/null @@ -1,299 +0,0 @@ -package me.funkin.fnf; - -import android.annotation.SuppressLint; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Point; -import android.os.CancellationSignal; -import android.os.ParcelFileDescriptor; -import android.provider.DocumentsProvider; -import android.provider.DocumentsContract.Root; -import android.provider.DocumentsContract.Document; -import android.util.Log; -import android.webkit.MimeTypeMap; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Path; - -public class ModFolderProvider extends DocumentsProvider -{ - private static final String TAG = "ModFolderProvider"; - private File rootDir; - private static final String[] DEFAULT_ROOT_PROJECTION = - new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, - Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, - Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, - Root.COLUMN_AVAILABLE_BYTES,}; - private static final String[] DEFAULT_DOCUMENT_PROJECTION = new - String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, - Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, - Document.COLUMN_FLAGS, Document.COLUMN_SIZE,}; - - private static final String ROOT = "root"; - - @Override - public Cursor queryRoots(String[] projection) throws FileNotFoundException - { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION); - - final MatrixCursor.RowBuilder row = result.newRow(); - row.add(Root.COLUMN_ROOT_ID, ROOT); - row.add(Root.COLUMN_DOCUMENT_ID, getDocumentID(rootDir)); - row.add(Root.COLUMN_TITLE, "Friday Night Funkin'"); - row.add(Root.COLUMN_SUMMARY, "Mods folder"); - row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD); - row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); - - return result; - } - - @Override - public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException - { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); - - includeFile(result, getFileFromDocumentID(documentId)); - return result; - } - - @Override - public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException - { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); - - final File parent = getFileFromDocumentID(parentDocumentId); - - for (File file : parent.listFiles()) - { - includeFile(result, file); - } - - return result; - } - - @Override - public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException - { - return ParcelFileDescriptor.open(getFileFromDocumentID(documentId), ParcelFileDescriptor.parseMode(mode)); - } - - @SuppressLint({"SetWorldReadable", "SetWorldWritable"}) - @Override - public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException - { - File parentPath = getFileFromDocumentID(parentDocumentId); - try - { - File file = new File(parentPath, displayName); - if (mimeType.equals(Document.MIME_TYPE_DIR)) - assert file.mkdir(); - else - assert file.createNewFile(); - assert file.setReadable(true, false); - assert file.setWritable(true, false); - - return getDocumentID(file); - } - catch (IOException e) - { - throw new FileNotFoundException("Cannot create file with id " + parentDocumentId + "/" + displayName); - } - } - - @Override - public void deleteDocument(String documentId) throws FileNotFoundException - { - File file = getFileFromDocumentID(documentId); - - Boolean result = false; - - try - { - if (file.isDirectory()) - { - result = deleteFolderRecursively(file); - } - else - { - result = file.delete(); - } - } - catch (Exception e) - { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - - if (!result) - { - throw new FileNotFoundException("Failed to delete document with id " + documentId); - } - } - - public boolean deleteFolderRecursively(File folder) - { - boolean isSuccessful = true; - - final File[] files = folder.listFiles(); - - if (files == null || !folder.exists()) - return false; - - for (File item : files) - { - if (item.isDirectory()) - { - isSuccessful = isSuccessful && deleteFolderRecursively(item); - } - else - { - isSuccessful = isSuccessful && item.delete(); - } - } - - isSuccessful = isSuccessful && folder.delete(); - - return isSuccessful; - } - - @Override - public String renameDocument(String documentId, String displayName) throws FileNotFoundException - { - File origin = getFileFromDocumentID(documentId); - File dest = new File(origin.getParentFile(), displayName); - - if (!origin.renameTo(dest)) - { - throw new FileNotFoundException("Failed to rename document with id " + documentId + " to " + displayName); - } - - return getDocumentID(dest); - } - - @Override - public boolean isChildDocument(String parentDocumentId, String documentId) - { - return documentId.startsWith(parentDocumentId); - } - - @Override - public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException - { - final File file = getFileFromDocumentID(documentId); - final ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - return new AssetFileDescriptor(parcelFileDescriptor, 0, AssetFileDescriptor.UNKNOWN_LENGTH); - } - - private String getDocumentID(File file) - { - Path filePath = file.toPath().normalize(); - Path rootPath = rootDir.toPath(); - - if (!filePath.startsWith(rootPath)) - return null; - - return ROOT + ":" + rootPath.relativize(filePath).toString(); - } - - private File getFileFromDocumentID(String id) - { - final String[] separatedPath = id.split(":", 2); - - if (separatedPath.length < 2) - return null; - - if (!separatedPath[0].equals(ROOT)) - return null; - - final String filePath = separatedPath[1]; - - if (filePath.isEmpty()) - return rootDir; - - final Path rootPath = rootDir.toPath().normalize().toAbsolutePath(); - final Path desiredPath = rootPath.resolve(filePath).normalize().toAbsolutePath(); - - if (!desiredPath.startsWith(rootPath)) - { - return null; - } - - return desiredPath.toFile(); - } - - private void includeFile(MatrixCursor result, File file) - { - if (file == null) - file = rootDir; - - int flags = 0; - final String mimeType = getTypeForFile(file); - - if (file.canWrite()) - { - flags |= Document.FLAG_SUPPORTS_WRITE | - Document.FLAG_SUPPORTS_DELETE | - Document.FLAG_SUPPORTS_RENAME; - - if (file.isDirectory()) - flags |= Document.FLAG_DIR_SUPPORTS_CREATE; - } - - if (mimeType.startsWith("image/")) - { - flags |= Document.FLAG_SUPPORTS_THUMBNAIL; - } - - final MatrixCursor.RowBuilder row = result.newRow(); - row.add(Document.COLUMN_DOCUMENT_ID, getDocumentID(file)); - row.add(Document.COLUMN_DISPLAY_NAME, file.getName()); - row.add(Document.COLUMN_SIZE, file.length()); - row.add(Document.COLUMN_MIME_TYPE, mimeType); - row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified()); - row.add(Document.COLUMN_FLAGS, flags); - } - - private static String getTypeForFile(File file) - { - if (file.isDirectory()) - { - return Document.MIME_TYPE_DIR; - } - else - { - return getTypeForName(file.getName()); - } - } - - private static String getTypeForName(String name) - { - final int lastDot = name.lastIndexOf('.'); - if (lastDot >= 0) - { - final String extension = name.substring(lastDot + 1); - - if (extension.equals("hxc") || extension.equals("hx")) - return "application/*"; - - final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - if (mime != null) - { - return mime; - } - } - return "application/octet-stream"; - } - - @Override - public boolean onCreate() - { - rootDir = getContext().getObbDir(); - return true; - } -}