Some adjustments
This commit is contained in:
committed by
Mihai Alexandru
parent
f91261f748
commit
314d8af9c8
+20
-6
@@ -12,7 +12,15 @@ class CallbackUtil
|
||||
/**
|
||||
* The result code for `DATA_FOLDER_CLOSED` activity.
|
||||
*/
|
||||
public static final DATA_FOLDER_CLOSED:Int = JNI.createStaticField('funkin/extensions/CallbackUtil', 'DATA_FOLDER_CLOSED', 'I').get();
|
||||
public static var DATA_FOLDER_CLOSED(get, never):Int;
|
||||
|
||||
@:noCompletion
|
||||
static function get_DATA_FOLDER_CLOSED():Int
|
||||
{
|
||||
final field:Null<Dynamic> = JNIUtil.createStaticField('funkin/extensions/CallbackUtil', 'DATA_FOLDER_CLOSED', 'I');
|
||||
|
||||
return field != null ? field.get() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal triggered when an activity result is received.
|
||||
@@ -21,21 +29,27 @@ class CallbackUtil
|
||||
*/
|
||||
public static var onActivityResult:FlxTypedSignal<Int->Int->Void> = new FlxTypedSignal<Int->Int->Void>();
|
||||
|
||||
/**
|
||||
* Initializes the callback utility.
|
||||
*/
|
||||
public static function init():Void
|
||||
{
|
||||
final initCallBackJNI:Null<Dynamic> = JNI.createStaticMethod('funkin/extensions/CallbackUtil', 'initCallBack', '(Lorg/haxe/lime/HaxeObject;)V');
|
||||
final initCallBackJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/extensions/CallbackUtil', 'initCallBack', '(Lorg/haxe/lime/HaxeObject;)V');
|
||||
|
||||
if (initCallBackJNI != null) initCallBackJNI(new CallBackHandler());
|
||||
if (initCallBackJNI != null)
|
||||
{
|
||||
initCallBackJNI(new CallbackHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to handle native callback events.
|
||||
*/
|
||||
@:noCompletion
|
||||
private class CallBackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
||||
class CallbackHandler #if (lime >= "8.0.0") implements JNISafety #end
|
||||
{
|
||||
public function new():Void {}
|
||||
@:allow(funkin.mobile.external.android.CallbackUtil)
|
||||
function new():Void {}
|
||||
|
||||
/**
|
||||
* Handles the activity result callback from native code.
|
||||
|
||||
+8
-21
@@ -1,34 +1,21 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A Utility class to manage the Application's Data folder on Android.
|
||||
*/
|
||||
@:unreflective
|
||||
class DataFolderUtil
|
||||
{
|
||||
@:noCompletion
|
||||
static var staticMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
/**
|
||||
* Opens the data folder on an Android device using JNI.
|
||||
*/
|
||||
public static function openDataFolder():Void
|
||||
{
|
||||
final openDataFolderJNI:Null<Dynamic> = createStaticMethod('funkin/util/DataFolderUtil', 'openDataFolder', '()V');
|
||||
final openDataFolderJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/DataFolderUtil', 'openDataFolder', '(I)V');
|
||||
|
||||
if (openDataFolderJNI != null) openDataFolderJNI();
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@: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);
|
||||
if (openDataFolderJNI != null)
|
||||
{
|
||||
openDataFolderJNI(CallbackUtil.DATA_FOLDER_CLOSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
#if android
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A utility class for caching JNI method and field references.
|
||||
*/
|
||||
class JNIUtil
|
||||
{
|
||||
@:noCompletion
|
||||
private static var staticMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var staticFieldCache:Map<String, JNIStaticField> = [];
|
||||
|
||||
@:noCompletion
|
||||
private static var memberFieldCache:Map<String, JNIMemberField> = [];
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the static method.
|
||||
*/
|
||||
public static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@: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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member method reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the method.
|
||||
* @param methodName The name of the method.
|
||||
* @param signature The method signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A dynamic reference to the member method.
|
||||
*/
|
||||
public static function createMemberMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$methodName::$signature';
|
||||
|
||||
if (cache && !memberMethodCache.exists(key)) memberMethodCache.set(key, JNI.createMemberMethod(className, methodName, signature));
|
||||
else if (!cache) return JNI.createMemberMethod(className, methodName, signature);
|
||||
|
||||
return memberMethodCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached static field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the static field.
|
||||
*/
|
||||
public static function createStaticField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIStaticField>
|
||||
{
|
||||
@:privateAccess
|
||||
className = JNI.transformClassName(className);
|
||||
|
||||
final key:String = '$className::$fieldName::$signature';
|
||||
|
||||
if (cache && !staticFieldCache.exists(key)) staticFieldCache.set(key, JNI.createStaticField(className, fieldName, signature));
|
||||
else if (!cache) return JNI.createStaticField(className, fieldName, signature);
|
||||
|
||||
return staticFieldCache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates a cached member field reference.
|
||||
*
|
||||
* @param className The name of the Java class containing the field.
|
||||
* @param fieldName The name of the field.
|
||||
* @param signature The field signature in JNI format.
|
||||
* @param cache Whether to cache the result (default true).
|
||||
* @return A reference to the member field.
|
||||
*/
|
||||
public static function createMemberField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIMemberField>
|
||||
{
|
||||
@: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);
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,50 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A Utility class to get Android screen related informations.
|
||||
*/
|
||||
@:unreflective
|
||||
class ScreenUtil
|
||||
{
|
||||
/**
|
||||
* Retrieves the dimensions of display cutouts (such as notches) on Android devices.
|
||||
*
|
||||
* @return An array of `Rectangle` objects, each representing a display cutout's position and size.
|
||||
*/
|
||||
public static function getCutoutDimensions():Array<Rectangle>
|
||||
{
|
||||
final getCutoutDimensionsJNI:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/ScreenUtil', 'getCutoutDimensions', '()[Landroid/graphics/Rect;');
|
||||
|
||||
if (getCutoutDimensionsJNI != null)
|
||||
{
|
||||
final rectangles:Array<Rectangle> = [];
|
||||
|
||||
for (rectangle in cast(getCutoutDimensionsJNI(), Array<Dynamic>))
|
||||
{
|
||||
if (rectangle == null) continue;
|
||||
|
||||
final topJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'top', 'I');
|
||||
final leftJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'left', 'I');
|
||||
final rightJNI:Null<JNIMemberField> = JNIUtil.createMemberField('android/graphics/Rect', 'right', 'I');
|
||||
final bottomJNI:Null<JNIMemberField> = JNIUtil.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 [];
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package funkin.mobile.external.android;
|
||||
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* A Utility class to get Android screen related informations.
|
||||
*/
|
||||
@:unreflective
|
||||
class ScreenUtils
|
||||
{
|
||||
@:noCompletion
|
||||
static var staticMethodCache:Map<String, Dynamic> = [];
|
||||
|
||||
@:noCompletion
|
||||
static var memberFieldCache:Map<String, JNIMemberField> = [];
|
||||
|
||||
public static function getCutoutDimensions():Array<Rectangle>
|
||||
{
|
||||
final getCutoutDimensionsJNI:Null<Dynamic> = createStaticMethod('funkin/util/ScreenUtils', 'getCutoutDimensions', '()[Landroid/graphics/Rect;');
|
||||
|
||||
if (getCutoutDimensionsJNI != null)
|
||||
{
|
||||
final rectangles:Array<Rectangle> = [];
|
||||
|
||||
for (rectangle in cast(getCutoutDimensionsJNI(), Array<Dynamic>))
|
||||
{
|
||||
if (rectangle == null) continue;
|
||||
|
||||
final topJNI:Null<JNIMemberField> = createMemberField('android/graphics/Rect', 'top', 'I');
|
||||
final leftJNI:Null<JNIMemberField> = createMemberField('android/graphics/Rect', 'left', 'I');
|
||||
final rightJNI:Null<JNIMemberField> = createMemberField('android/graphics/Rect', 'right', 'I');
|
||||
final bottomJNI:Null<JNIMemberField> = 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 [];
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
static function createStaticMethod(className:String, methodName:String, signature:String, cache:Bool = true):Null<Dynamic>
|
||||
{
|
||||
@: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);
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
static function createMemberField(className:String, fieldName:String, signature:String, cache:Bool = true):Null<JNIMemberField>
|
||||
{
|
||||
@: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);
|
||||
}
|
||||
}
|
||||
+11
-20
@@ -3,42 +3,33 @@ package funkin.extensions;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
import org.haxe.lime.HaxeObject;
|
||||
|
||||
public class CallbackUtil extends Extension
|
||||
{
|
||||
public static HaxeObject cbObject;
|
||||
|
||||
/**
|
||||
* Constant representing the event when the data folder is closed.
|
||||
*/
|
||||
public static int DATA_FOLDER_CLOSED = 0x01;
|
||||
|
||||
private static HaxeObject haxeObject;
|
||||
|
||||
/**
|
||||
* Initializes the callback object for handling Haxe callbacks.
|
||||
*
|
||||
* @param cbObject The HaxeObject instance to handle callbacks.
|
||||
* @param haxeObject The HaxeObject instance to handle callbacks.
|
||||
*/
|
||||
public static void initCallBack(final HaxeObject cbObject)
|
||||
public static void initCallBack(final HaxeObject haxeObject)
|
||||
{
|
||||
CallbackUtil.cbObject = cbObject;
|
||||
CallbackUtil.haxeObject = haxeObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (cbObject != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object[] args = new Object[2];
|
||||
args[0] = requestCode;
|
||||
args[1] = resultCode;
|
||||
|
||||
cbObject.call("onActivityResult", args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (haxeObject != null)
|
||||
haxeObject.call2("onActivityResult", requestCode, resultCode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+5
-33
@@ -7,13 +7,10 @@ import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import funkin.extensions.CallbackUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
|
||||
public class DataFolderUtil
|
||||
{
|
||||
/**
|
||||
@@ -21,41 +18,16 @@ public class DataFolderUtil
|
||||
* It's highly based on some code borrowed from Mterial Files
|
||||
* https://github.com/zhanghai/MaterialFiles
|
||||
*/
|
||||
public static void openDataFolder()
|
||||
public static void openDataFolder(int requestCode)
|
||||
{
|
||||
::if (APP_PACKAGE != "")::
|
||||
if (Extension.mainActivity != null)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(DocumentsContract.buildRootUri("::APP_PACKAGE::.docprovider", ""), "vnd.android.document/directory");
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
String documentsUiPackage = getDocumentsUiPackage();
|
||||
if (documentsUiPackage != null) {
|
||||
intent.setPackage(documentsUiPackage);
|
||||
Extension.mainActivity.startActivityForResult(intent, CallbackUtil.DATA_FOLDER_CLOSED);
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(DocumentsContract.buildRootUri("::APP_PACKAGE::.docprovider", ""), "vnd.android.document/directory");
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
Extension.mainActivity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
::end::
|
||||
}
|
||||
|
||||
private static String getDocumentsUiPackage() {
|
||||
List<PackageInfo> packageInfos = Extension.mainContext.getPackageManager().getPackagesHoldingPermissions(
|
||||
new String[]{"android.permission.MANAGE_DOCUMENTS"}, 0
|
||||
);
|
||||
|
||||
PackageInfo targetPackage = null;
|
||||
for (PackageInfo pkg : packageInfos) {
|
||||
if (pkg.packageName.endsWith(".documentsui")) {
|
||||
targetPackage = pkg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPackage == null && !packageInfos.isEmpty()) {
|
||||
targetPackage = packageInfos.get(0);
|
||||
}
|
||||
|
||||
return targetPackage != null ? targetPackage.packageName : null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class ScreenUtils
|
||||
public class ScreenUtil
|
||||
{
|
||||
public static Rect[] getCutoutDimensions()
|
||||
{
|
||||
Vendored
+2
-2
@@ -4,9 +4,9 @@ package funkin.mobile.external.ios;
|
||||
* A Utility class to get iOS screen related informations.
|
||||
*/
|
||||
@:build(funkin.mobile.macros.LinkerMacro.xml('project/Build.xml'))
|
||||
@:include('ScreenUtils.hpp')
|
||||
@:include('ScreenUtil.hpp')
|
||||
@:unreflective
|
||||
extern class ScreenUtils
|
||||
extern class ScreenUtil
|
||||
{
|
||||
@:native('getSafeAreaInsets')
|
||||
static function getSafeAreaInsets(top:cpp.RawPointer<Float>, bottom:cpp.RawPointer<Float>, left:cpp.RawPointer<Float>, right:cpp.RawPointer<Float>):Void;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
<files id="external-ios" dir="${this_dir}/ios" if="iphoneos || iphonesim">
|
||||
<compilerflag value="-I${this_dir}/ios/include" />
|
||||
|
||||
<file name="src/ScreenUtils.mm" />
|
||||
<file name="src/ScreenUtil.mm" />
|
||||
<file name="src/AudioSession.mm" />
|
||||
</files>
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#import "ScreenUtils.hpp"
|
||||
#import "ScreenUtil.hpp"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -1,9 +1,9 @@
|
||||
package funkin.mobile.util;
|
||||
|
||||
#if ios
|
||||
import funkin.mobile.external.ios.ScreenUtils;
|
||||
import funkin.mobile.external.ios.ScreenUtil as NativeScreenUtil;
|
||||
#elseif android
|
||||
import funkin.mobile.external.android.ScreenUtils;
|
||||
import funkin.mobile.external.android.ScreenUtil as NativeScreenUtil;
|
||||
#end
|
||||
import lime.math.Rectangle;
|
||||
import lime.system.System;
|
||||
@@ -26,7 +26,7 @@ class ScreenUtil
|
||||
final rectDimensions:Array<Array<Float>> = [[], [], [], []];
|
||||
|
||||
// Push all the dimensions of the cutouts into an array
|
||||
for (rect in ScreenUtils.getCutoutDimensions())
|
||||
for (rect in NativeScreenUtil.getCutoutDimensions())
|
||||
{
|
||||
rectDimensions[0].push(rect.x);
|
||||
rectDimensions[1].push(rect.y);
|
||||
@@ -59,13 +59,12 @@ class ScreenUtil
|
||||
var bottomInset:Float = -1;
|
||||
var deviceWidth:Float = -1;
|
||||
var deviceHeight:Float = -1;
|
||||
var displayOrientation:DisplayOrientation = System.getDisplayOrientation(0);
|
||||
|
||||
ScreenUtils.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
NativeScreenUtil.getSafeAreaInsets(cpp.RawPointer.addressOf(topInset), cpp.RawPointer.addressOf(bottomInset), cpp.RawPointer.addressOf(leftInset),
|
||||
cpp.RawPointer.addressOf(rightInset));
|
||||
|
||||
ScreenUtils.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
var displayOrientation = System.getDisplayOrientation(0);
|
||||
NativeScreenUtil.getScreenSize(cpp.RawPointer.addressOf(deviceWidth), cpp.RawPointer.addressOf(deviceHeight));
|
||||
|
||||
notchRect.x = 0;
|
||||
notchRect.y = 0.0;
|
||||
|
||||
@@ -69,6 +69,7 @@ class ReloadAssetsDebugPlugin extends FlxBasic
|
||||
}
|
||||
}
|
||||
|
||||
#if android
|
||||
@:noCompletion
|
||||
function onActivityResult(resultCode:Int, requestCode:Int):Void
|
||||
{
|
||||
@@ -77,4 +78,5 @@ class ReloadAssetsDebugPlugin extends FlxBasic
|
||||
reload();
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user