调用相册测试
This commit is contained in:
parent
394624d71a
commit
b33ea25402
9
TheStrongestSnail/Assets/Plugins/NativeGallery.meta
Normal file
9
TheStrongestSnail/Assets/Plugins/NativeGallery.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e05ed2bddbccb94e9650efb5742e452
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1518877529
|
||||||
|
licenseType: Store
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a607dcda26e7614f86300c6ca717295
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1498722617
|
||||||
|
licenseType: Store
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,38 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_ANDROID
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGCallbackHelper : MonoBehaviour
|
||||||
|
{
|
||||||
|
private System.Action mainThreadAction = null;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
DontDestroyOnLoad( gameObject );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if( mainThreadAction != null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.Action temp = mainThreadAction;
|
||||||
|
mainThreadAction = null;
|
||||||
|
temp();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Destroy( gameObject );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CallOnMainThread( System.Action function )
|
||||||
|
{
|
||||||
|
mainThreadAction = function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2d517fd0f2f85f24698df2775bee58e9
|
||||||
|
timeCreated: 1544889149
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,62 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_ANDROID
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGMediaReceiveCallbackAndroid : AndroidJavaProxy
|
||||||
|
{
|
||||||
|
private readonly NativeGallery.MediaPickCallback callback;
|
||||||
|
private readonly NativeGallery.MediaPickMultipleCallback callbackMultiple;
|
||||||
|
|
||||||
|
private readonly NGCallbackHelper callbackHelper;
|
||||||
|
|
||||||
|
public NGMediaReceiveCallbackAndroid( NativeGallery.MediaPickCallback callback, NativeGallery.MediaPickMultipleCallback callbackMultiple ) : base( "com.yasirkula.unity.NativeGalleryMediaReceiver" )
|
||||||
|
{
|
||||||
|
this.callback = callback;
|
||||||
|
this.callbackMultiple = callbackMultiple;
|
||||||
|
callbackHelper = new GameObject( "NGCallbackHelper" ).AddComponent<NGCallbackHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMediaReceived( string path )
|
||||||
|
{
|
||||||
|
callbackHelper.CallOnMainThread( () => callback( !string.IsNullOrEmpty( path ) ? path : null ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMultipleMediaReceived( string paths )
|
||||||
|
{
|
||||||
|
string[] result = null;
|
||||||
|
if( !string.IsNullOrEmpty( paths ) )
|
||||||
|
{
|
||||||
|
string[] pathsSplit = paths.Split( '>' );
|
||||||
|
|
||||||
|
int validPathCount = 0;
|
||||||
|
for( int i = 0; i < pathsSplit.Length; i++ )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrEmpty( pathsSplit[i] ) )
|
||||||
|
validPathCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( validPathCount == 0 )
|
||||||
|
pathsSplit = new string[0];
|
||||||
|
else if( validPathCount != pathsSplit.Length )
|
||||||
|
{
|
||||||
|
string[] validPaths = new string[validPathCount];
|
||||||
|
for( int i = 0, j = 0; i < pathsSplit.Length; i++ )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrEmpty( pathsSplit[i] ) )
|
||||||
|
validPaths[j++] = pathsSplit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
pathsSplit = validPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = pathsSplit;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackHelper.CallOnMainThread( () => callbackMultiple( ( result != null && result.Length > 0 ) ? result : null ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c18d702b07a63945968db47201b95c9
|
||||||
|
timeCreated: 1519060539
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,48 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_ANDROID
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGPermissionCallbackAndroid : AndroidJavaProxy
|
||||||
|
{
|
||||||
|
private object threadLock;
|
||||||
|
public int Result { get; private set; }
|
||||||
|
|
||||||
|
public NGPermissionCallbackAndroid( object threadLock ) : base( "com.yasirkula.unity.NativeGalleryPermissionReceiver" )
|
||||||
|
{
|
||||||
|
Result = -1;
|
||||||
|
this.threadLock = threadLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnPermissionResult( int result )
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
|
||||||
|
lock( threadLock )
|
||||||
|
{
|
||||||
|
Monitor.Pulse( threadLock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NGPermissionCallbackAsyncAndroid : AndroidJavaProxy
|
||||||
|
{
|
||||||
|
private readonly NativeGallery.PermissionCallback callback;
|
||||||
|
private readonly NGCallbackHelper callbackHelper;
|
||||||
|
|
||||||
|
public NGPermissionCallbackAsyncAndroid( NativeGallery.PermissionCallback callback ) : base( "com.yasirkula.unity.NativeGalleryPermissionReceiver" )
|
||||||
|
{
|
||||||
|
this.callback = callback;
|
||||||
|
callbackHelper = new GameObject( "NGCallbackHelper" ).AddComponent<NGCallbackHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnPermissionResult( int result )
|
||||||
|
{
|
||||||
|
callbackHelper.CallOnMainThread( () => callback( (NativeGallery.Permission) result ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a07afac614af1294d8e72a3c083be028
|
||||||
|
timeCreated: 1519060539
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,33 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: db4d55e1212537e4baa84cac66eb6645
|
||||||
|
timeCreated: 1569764737
|
||||||
|
licenseType: Store
|
||||||
|
PluginImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
platformData:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Android: Android
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 19fc6b8ce781591438a952d8aa9104f8
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1521452097
|
||||||
|
licenseType: Store
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,152 @@
|
|||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
#if UNITY_IOS
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
|
using UnityEditor.iOS.Xcode;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
[System.Serializable]
|
||||||
|
public class Settings
|
||||||
|
{
|
||||||
|
private const string SAVE_PATH = "ProjectSettings/NativeGallery.json";
|
||||||
|
|
||||||
|
public bool AutomatedSetup = true;
|
||||||
|
#if !UNITY_2018_1_OR_NEWER
|
||||||
|
public bool MinimumiOSTarget8OrAbove = false;
|
||||||
|
#endif
|
||||||
|
public string PhotoLibraryUsageDescription = "The app requires access to Photos to interact with it.";
|
||||||
|
public string PhotoLibraryAdditionsUsageDescription = "The app requires access to Photos to save media to it.";
|
||||||
|
public bool DontAskLimitedPhotosPermissionAutomaticallyOnIos14 = true; // See: https://mackuba.eu/2020/07/07/photo-library-changes-ios-14/
|
||||||
|
|
||||||
|
private static Settings m_instance = null;
|
||||||
|
public static Settings Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_instance == null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( File.Exists( SAVE_PATH ) )
|
||||||
|
m_instance = JsonUtility.FromJson<Settings>( File.ReadAllText( SAVE_PATH ) );
|
||||||
|
else
|
||||||
|
m_instance = new Settings();
|
||||||
|
}
|
||||||
|
catch( System.Exception e )
|
||||||
|
{
|
||||||
|
Debug.LogException( e );
|
||||||
|
m_instance = new Settings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
File.WriteAllText( SAVE_PATH, JsonUtility.ToJson( this, true ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2018_3_OR_NEWER
|
||||||
|
[SettingsProvider]
|
||||||
|
public static SettingsProvider CreatePreferencesGUI()
|
||||||
|
{
|
||||||
|
return new SettingsProvider( "Project/yasirkula/Native Gallery", SettingsScope.Project )
|
||||||
|
{
|
||||||
|
guiHandler = ( searchContext ) => PreferencesGUI(),
|
||||||
|
keywords = new System.Collections.Generic.HashSet<string>() { "Native", "Gallery", "Android", "iOS" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !UNITY_2018_3_OR_NEWER
|
||||||
|
[PreferenceItem( "Native Gallery" )]
|
||||||
|
#endif
|
||||||
|
public static void PreferencesGUI()
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
Instance.AutomatedSetup = EditorGUILayout.Toggle( "Automated Setup", Instance.AutomatedSetup );
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup( !Instance.AutomatedSetup );
|
||||||
|
#if !UNITY_2018_1_OR_NEWER
|
||||||
|
Instance.MinimumiOSTarget8OrAbove = EditorGUILayout.Toggle( "Deployment Target Is 8.0 Or Above", Instance.MinimumiOSTarget8OrAbove );
|
||||||
|
#endif
|
||||||
|
Instance.PhotoLibraryUsageDescription = EditorGUILayout.DelayedTextField( "Photo Library Usage Description", Instance.PhotoLibraryUsageDescription );
|
||||||
|
Instance.PhotoLibraryAdditionsUsageDescription = EditorGUILayout.DelayedTextField( "Photo Library Additions Usage Description", Instance.PhotoLibraryAdditionsUsageDescription );
|
||||||
|
Instance.DontAskLimitedPhotosPermissionAutomaticallyOnIos14 = EditorGUILayout.Toggle( new GUIContent( "Don't Ask Limited Photos Permission Automatically", "See: https://mackuba.eu/2020/07/07/photo-library-changes-ios-14/. It's recommended to keep this setting enabled" ), Instance.DontAskLimitedPhotosPermissionAutomaticallyOnIos14 );
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
if( EditorGUI.EndChangeCheck() )
|
||||||
|
Instance.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NGPostProcessBuild
|
||||||
|
{
|
||||||
|
#if UNITY_IOS
|
||||||
|
[PostProcessBuild( 1 )]
|
||||||
|
public static void OnPostprocessBuild( BuildTarget target, string buildPath )
|
||||||
|
{
|
||||||
|
if( !Settings.Instance.AutomatedSetup )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( target == BuildTarget.iOS )
|
||||||
|
{
|
||||||
|
string pbxProjectPath = PBXProject.GetPBXProjectPath( buildPath );
|
||||||
|
string plistPath = Path.Combine( buildPath, "Info.plist" );
|
||||||
|
|
||||||
|
PBXProject pbxProject = new PBXProject();
|
||||||
|
pbxProject.ReadFromFile( pbxProjectPath );
|
||||||
|
|
||||||
|
#if UNITY_2019_3_OR_NEWER
|
||||||
|
string targetGUID = pbxProject.GetUnityFrameworkTargetGuid();
|
||||||
|
#else
|
||||||
|
string targetGUID = pbxProject.TargetGuidByName( PBXProject.GetUnityTargetName() );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Minimum supported iOS version on Unity 2018.1 and later is 8.0
|
||||||
|
#if !UNITY_2018_1_OR_NEWER
|
||||||
|
if( !Settings.Instance.MinimumiOSTarget8OrAbove )
|
||||||
|
{
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-weak_framework Photos" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-weak_framework PhotosUI" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework AssetsLibrary" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework MobileCoreServices" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework ImageIO" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-weak_framework PhotosUI" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework Photos" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework MobileCoreServices" );
|
||||||
|
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework ImageIO" );
|
||||||
|
}
|
||||||
|
|
||||||
|
pbxProject.RemoveFrameworkFromProject( targetGUID, "Photos.framework" );
|
||||||
|
pbxProject.RemoveFrameworkFromProject( targetGUID, "PhotosUI.framework" );
|
||||||
|
|
||||||
|
File.WriteAllText( pbxProjectPath, pbxProject.WriteToString() );
|
||||||
|
|
||||||
|
PlistDocument plist = new PlistDocument();
|
||||||
|
plist.ReadFromString( File.ReadAllText( plistPath ) );
|
||||||
|
|
||||||
|
PlistElementDict rootDict = plist.root;
|
||||||
|
if( !string.IsNullOrEmpty( Settings.Instance.PhotoLibraryUsageDescription ) )
|
||||||
|
rootDict.SetString( "NSPhotoLibraryUsageDescription", Settings.Instance.PhotoLibraryUsageDescription );
|
||||||
|
if( !string.IsNullOrEmpty( Settings.Instance.PhotoLibraryAdditionsUsageDescription ) )
|
||||||
|
rootDict.SetString( "NSPhotoLibraryAddUsageDescription", Settings.Instance.PhotoLibraryAdditionsUsageDescription );
|
||||||
|
if( Settings.Instance.DontAskLimitedPhotosPermissionAutomaticallyOnIos14 )
|
||||||
|
rootDict.SetBoolean( "PHPhotoLibraryPreventAutomaticLimitedAccessAlert", true );
|
||||||
|
|
||||||
|
File.WriteAllText( plistPath, plist.WriteToString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dff1540cf22bfb749a2422f445cf9427
|
||||||
|
timeCreated: 1521452119
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "NativeGallery.Editor",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3dffc8e654f00c545a82d0a5274d51eb
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name": "NativeGallery.Runtime"
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6e5063adab271564ba0098a06a8cebda
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
1039
TheStrongestSnail/Assets/Plugins/NativeGallery/NativeGallery.cs
Normal file
1039
TheStrongestSnail/Assets/Plugins/NativeGallery/NativeGallery.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ce1403606c3629046a0147d3e705f7cc
|
||||||
|
timeCreated: 1498722610
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
|||||||
|
= Native Gallery for Android & iOS (v1.8.1) =
|
||||||
|
|
||||||
|
Documentation: https://github.com/yasirkula/UnityNativeGallery
|
||||||
|
FAQ: https://github.com/yasirkula/UnityNativeGallery#faq
|
||||||
|
Example code: https://github.com/yasirkula/UnityNativeGallery#example-code
|
||||||
|
E-mail: yasirkula@gmail.com
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: be769f45b807c40459e5bafb18e887d6
|
||||||
|
timeCreated: 1563308465
|
||||||
|
licenseType: Store
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
9
TheStrongestSnail/Assets/Plugins/NativeGallery/iOS.meta
Normal file
9
TheStrongestSnail/Assets/Plugins/NativeGallery/iOS.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c623599351a41a4c84c20f73c9d8976
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1498722622
|
||||||
|
licenseType: Store
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,132 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_IOS
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGMediaReceiveCallbackiOS : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static NGMediaReceiveCallbackiOS instance;
|
||||||
|
|
||||||
|
private NativeGallery.MediaPickCallback callback;
|
||||||
|
private NativeGallery.MediaPickMultipleCallback callbackMultiple;
|
||||||
|
|
||||||
|
private float nextBusyCheckTime;
|
||||||
|
|
||||||
|
public static bool IsBusy { get; private set; }
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||||
|
private static extern int _NativeGallery_IsMediaPickerBusy();
|
||||||
|
|
||||||
|
public static void Initialize( NativeGallery.MediaPickCallback callback, NativeGallery.MediaPickMultipleCallback callbackMultiple )
|
||||||
|
{
|
||||||
|
if( IsBusy )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( instance == null )
|
||||||
|
{
|
||||||
|
instance = new GameObject( "NGMediaReceiveCallbackiOS" ).AddComponent<NGMediaReceiveCallbackiOS>();
|
||||||
|
DontDestroyOnLoad( instance.gameObject );
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.callback = callback;
|
||||||
|
instance.callbackMultiple = callbackMultiple;
|
||||||
|
|
||||||
|
instance.nextBusyCheckTime = Time.realtimeSinceStartup + 1f;
|
||||||
|
IsBusy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if( IsBusy )
|
||||||
|
{
|
||||||
|
if( Time.realtimeSinceStartup >= nextBusyCheckTime )
|
||||||
|
{
|
||||||
|
nextBusyCheckTime = Time.realtimeSinceStartup + 1f;
|
||||||
|
|
||||||
|
if( _NativeGallery_IsMediaPickerBusy() == 0 )
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
|
||||||
|
NativeGallery.MediaPickCallback _callback = callback;
|
||||||
|
callback = null;
|
||||||
|
|
||||||
|
NativeGallery.MediaPickMultipleCallback _callbackMultiple = callbackMultiple;
|
||||||
|
callbackMultiple = null;
|
||||||
|
|
||||||
|
if( _callback != null )
|
||||||
|
_callback( null );
|
||||||
|
|
||||||
|
if( _callbackMultiple != null )
|
||||||
|
_callbackMultiple( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMediaReceived( string path )
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
|
||||||
|
if( string.IsNullOrEmpty( path ) )
|
||||||
|
path = null;
|
||||||
|
|
||||||
|
NativeGallery.MediaPickCallback _callback = callback;
|
||||||
|
callback = null;
|
||||||
|
|
||||||
|
if( _callback != null )
|
||||||
|
_callback( path );
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMultipleMediaReceived( string paths )
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
|
||||||
|
string[] _paths = SplitPaths( paths );
|
||||||
|
if( _paths != null && _paths.Length == 0 )
|
||||||
|
_paths = null;
|
||||||
|
|
||||||
|
NativeGallery.MediaPickMultipleCallback _callbackMultiple = callbackMultiple;
|
||||||
|
callbackMultiple = null;
|
||||||
|
|
||||||
|
if( _callbackMultiple != null )
|
||||||
|
_callbackMultiple( _paths );
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] SplitPaths( string paths )
|
||||||
|
{
|
||||||
|
string[] result = null;
|
||||||
|
if( !string.IsNullOrEmpty( paths ) )
|
||||||
|
{
|
||||||
|
string[] pathsSplit = paths.Split( '>' );
|
||||||
|
|
||||||
|
int validPathCount = 0;
|
||||||
|
for( int i = 0; i < pathsSplit.Length; i++ )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrEmpty( pathsSplit[i] ) )
|
||||||
|
validPathCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( validPathCount == 0 )
|
||||||
|
pathsSplit = new string[0];
|
||||||
|
else if( validPathCount != pathsSplit.Length )
|
||||||
|
{
|
||||||
|
string[] validPaths = new string[validPathCount];
|
||||||
|
for( int i = 0, j = 0; i < pathsSplit.Length; i++ )
|
||||||
|
{
|
||||||
|
if( !string.IsNullOrEmpty( pathsSplit[i] ) )
|
||||||
|
validPaths[j++] = pathsSplit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
pathsSplit = validPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = pathsSplit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71fb861c149c2d1428544c601e52a33c
|
||||||
|
timeCreated: 1519060539
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,45 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_IOS
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGMediaSaveCallbackiOS : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static NGMediaSaveCallbackiOS instance;
|
||||||
|
private NativeGallery.MediaSaveCallback callback;
|
||||||
|
|
||||||
|
public static void Initialize( NativeGallery.MediaSaveCallback callback )
|
||||||
|
{
|
||||||
|
if( instance == null )
|
||||||
|
{
|
||||||
|
instance = new GameObject( "NGMediaSaveCallbackiOS" ).AddComponent<NGMediaSaveCallbackiOS>();
|
||||||
|
DontDestroyOnLoad( instance.gameObject );
|
||||||
|
}
|
||||||
|
else if( instance.callback != null )
|
||||||
|
instance.callback( false, null );
|
||||||
|
|
||||||
|
instance.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMediaSaveCompleted( string message )
|
||||||
|
{
|
||||||
|
NativeGallery.MediaSaveCallback _callback = callback;
|
||||||
|
callback = null;
|
||||||
|
|
||||||
|
if( _callback != null )
|
||||||
|
_callback( true, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnMediaSaveFailed( string error )
|
||||||
|
{
|
||||||
|
NativeGallery.MediaSaveCallback _callback = callback;
|
||||||
|
callback = null;
|
||||||
|
|
||||||
|
if( _callback != null )
|
||||||
|
_callback( false, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cbb865d0913a0d47bb6d2eb3ad04c4f
|
||||||
|
timeCreated: 1519060539
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,35 @@
|
|||||||
|
#if UNITY_EDITOR || UNITY_IOS
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NativeGalleryNamespace
|
||||||
|
{
|
||||||
|
public class NGPermissionCallbackiOS : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static NGPermissionCallbackiOS instance;
|
||||||
|
private NativeGallery.PermissionCallback callback;
|
||||||
|
|
||||||
|
public static void Initialize( NativeGallery.PermissionCallback callback )
|
||||||
|
{
|
||||||
|
if( instance == null )
|
||||||
|
{
|
||||||
|
instance = new GameObject( "NGPermissionCallbackiOS" ).AddComponent<NGPermissionCallbackiOS>();
|
||||||
|
DontDestroyOnLoad( instance.gameObject );
|
||||||
|
}
|
||||||
|
else if( instance.callback != null )
|
||||||
|
instance.callback( NativeGallery.Permission.ShouldAsk );
|
||||||
|
|
||||||
|
instance.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
|
public void OnPermissionRequested( string message )
|
||||||
|
{
|
||||||
|
NativeGallery.PermissionCallback _callback = callback;
|
||||||
|
callback = null;
|
||||||
|
|
||||||
|
if( _callback != null )
|
||||||
|
_callback( (NativeGallery.Permission) int.Parse( message ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc6d7fa0a99114a45b1a6800097c6eb1
|
||||||
|
timeCreated: 1519060539
|
||||||
|
licenseType: Store
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
1589
TheStrongestSnail/Assets/Plugins/NativeGallery/iOS/NativeGallery.mm
Normal file
1589
TheStrongestSnail/Assets/Plugins/NativeGallery/iOS/NativeGallery.mm
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,33 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 953e0b740eb03144883db35f72cad8a6
|
||||||
|
timeCreated: 1498722774
|
||||||
|
licenseType: Store
|
||||||
|
PluginImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
platformData:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
iPhone: iOS
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -12,6 +12,8 @@ public class EditPanel : Base
|
|||||||
public InputField UserInputField;
|
public InputField UserInputField;
|
||||||
public Text username;
|
public Text username;
|
||||||
public PerSonalCenterPanel perSonalCenterPanel;
|
public PerSonalCenterPanel perSonalCenterPanel;
|
||||||
|
|
||||||
|
public Button avatar;
|
||||||
// Start is called before the first frame update
|
// Start is called before the first frame update
|
||||||
async void Start()
|
async void Start()
|
||||||
{
|
{
|
||||||
@ -26,8 +28,50 @@ public class EditPanel : Base
|
|||||||
userInfomation14 = JsonConvert.DeserializeObject<UserInfomation14>(response14);
|
userInfomation14 = JsonConvert.DeserializeObject<UserInfomation14>(response14);
|
||||||
|
|
||||||
username.text = "" + userInfomation14.data.nickName;
|
username.text = "" + userInfomation14.data.nickName;
|
||||||
|
avatar.onClick.AddListener(selectavatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectavatar()
|
||||||
|
{
|
||||||
|
PickImage(512);
|
||||||
|
}
|
||||||
|
private void PickImage(int maxSize)
|
||||||
|
{
|
||||||
|
NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
|
||||||
|
{
|
||||||
|
Debug.Log("Image path: " + path);
|
||||||
|
if (path != null)
|
||||||
|
{
|
||||||
|
// Create Texture from selected image
|
||||||
|
Texture2D texture = NativeGallery.LoadImageAtPath(path, maxSize);
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
Debug.Log("Couldn't load texture from " + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign texture to a temporary quad and destroy it after 5 seconds
|
||||||
|
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||||
|
quad.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2.5f;
|
||||||
|
quad.transform.forward = Camera.main.transform.forward;
|
||||||
|
quad.transform.localScale = new Vector3(1f, texture.height / (float)texture.width, 1f);
|
||||||
|
|
||||||
|
Material material = quad.GetComponent<Renderer>().material;
|
||||||
|
if (!material.shader.isSupported) // happens when Standard shader is not included in the build
|
||||||
|
material.shader = Shader.Find("Legacy Shaders/Diffuse");
|
||||||
|
|
||||||
|
material.mainTexture = texture;
|
||||||
|
|
||||||
|
Destroy(quad, 5f);
|
||||||
|
|
||||||
|
// If a procedural texture is not destroyed manually,
|
||||||
|
// it will only be freed after a scene change
|
||||||
|
Destroy(texture, 5f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Debug.Log("Permission result: " + permission);
|
||||||
|
}
|
||||||
// Update is called once per frame
|
// Update is called once per frame
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
|
@ -2069,7 +2069,6 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 6661102261700243289}
|
- component: {fileID: 6661102261700243289}
|
||||||
- component: {fileID: 3692270011235859773}
|
- component: {fileID: 3692270011235859773}
|
||||||
- component: {fileID: 1821891998228383241}
|
|
||||||
- component: {fileID: 4775368350903003862}
|
- component: {fileID: 4775368350903003862}
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: PersonalCenterPanel
|
m_Name: PersonalCenterPanel
|
||||||
@ -2112,36 +2111,6 @@ CanvasRenderer:
|
|||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 3661722200123750880}
|
m_GameObject: {fileID: 3661722200123750880}
|
||||||
m_CullTransparentMesh: 1
|
m_CullTransparentMesh: 1
|
||||||
--- !u!114 &1821891998228383241
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 3661722200123750880}
|
|
||||||
m_Enabled: 0
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_Color: {r: 1, g: 1, b: 1, a: 0}
|
|
||||||
m_RaycastTarget: 1
|
|
||||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
|
||||||
m_Maskable: 1
|
|
||||||
m_OnCullStateChanged:
|
|
||||||
m_PersistentCalls:
|
|
||||||
m_Calls: []
|
|
||||||
m_Sprite: {fileID: 21300000, guid: c47381d146953bf419405b6a9a2f3f34, type: 3}
|
|
||||||
m_Type: 0
|
|
||||||
m_PreserveAspect: 0
|
|
||||||
m_FillCenter: 1
|
|
||||||
m_FillMethod: 4
|
|
||||||
m_FillAmount: 1
|
|
||||||
m_FillClockwise: 1
|
|
||||||
m_FillOrigin: 0
|
|
||||||
m_UseSpriteMesh: 0
|
|
||||||
m_PixelsPerUnitMultiplier: 1
|
|
||||||
--- !u!114 &4775368350903003862
|
--- !u!114 &4775368350903003862
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
@ -468,6 +468,7 @@ MonoBehaviour:
|
|||||||
UserInputField: {fileID: 379801035829787383}
|
UserInputField: {fileID: 379801035829787383}
|
||||||
username: {fileID: 8468291165135306074}
|
username: {fileID: 8468291165135306074}
|
||||||
perSonalCenterPanel: {fileID: 0}
|
perSonalCenterPanel: {fileID: 0}
|
||||||
|
avatar: {fileID: 2339787961763677450}
|
||||||
--- !u!1 &6960989442929760420
|
--- !u!1 &6960989442929760420
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@ -752,6 +753,7 @@ GameObject:
|
|||||||
- component: {fileID: 8996637367871962009}
|
- component: {fileID: 8996637367871962009}
|
||||||
- component: {fileID: 8996637367871962011}
|
- component: {fileID: 8996637367871962011}
|
||||||
- component: {fileID: 8996637367871962010}
|
- component: {fileID: 8996637367871962010}
|
||||||
|
- component: {fileID: 2339787961763677450}
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: Image
|
m_Name: Image
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@ -817,6 +819,50 @@ MonoBehaviour:
|
|||||||
m_FillOrigin: 0
|
m_FillOrigin: 0
|
||||||
m_UseSpriteMesh: 0
|
m_UseSpriteMesh: 0
|
||||||
m_PixelsPerUnitMultiplier: 1
|
m_PixelsPerUnitMultiplier: 1
|
||||||
|
--- !u!114 &2339787961763677450
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8996637367871962008}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Navigation:
|
||||||
|
m_Mode: 3
|
||||||
|
m_WrapAround: 0
|
||||||
|
m_SelectOnUp: {fileID: 0}
|
||||||
|
m_SelectOnDown: {fileID: 0}
|
||||||
|
m_SelectOnLeft: {fileID: 0}
|
||||||
|
m_SelectOnRight: {fileID: 0}
|
||||||
|
m_Transition: 1
|
||||||
|
m_Colors:
|
||||||
|
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||||
|
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||||
|
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||||
|
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||||
|
m_ColorMultiplier: 1
|
||||||
|
m_FadeDuration: 0.1
|
||||||
|
m_SpriteState:
|
||||||
|
m_HighlightedSprite: {fileID: 0}
|
||||||
|
m_PressedSprite: {fileID: 0}
|
||||||
|
m_SelectedSprite: {fileID: 0}
|
||||||
|
m_DisabledSprite: {fileID: 0}
|
||||||
|
m_AnimationTriggers:
|
||||||
|
m_NormalTrigger: Normal
|
||||||
|
m_HighlightedTrigger: Highlighted
|
||||||
|
m_PressedTrigger: Pressed
|
||||||
|
m_SelectedTrigger: Selected
|
||||||
|
m_DisabledTrigger: Disabled
|
||||||
|
m_Interactable: 1
|
||||||
|
m_TargetGraphic: {fileID: 8996637367871962010}
|
||||||
|
m_OnClick:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
--- !u!1 &8996637367919174697
|
--- !u!1 &8996637367919174697
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
Loading…
Reference in New Issue
Block a user