[Unity] SNS 공유하기 / Android N 대응 방법

Posted by Dev Park
2019. 5. 22. 06:12 Unity

SNS 공유하기 기능은 게임 앱 개발 시 많이 사용하는 기능중에 하나죠.

하지만 예전에 사용하던 코드가 Android N 부터는 정상작동을 하지 않고 에러가 나오더군요. 

 

그래서 공유하기 기능을 fileprovider 방식으로 변경해야 했습니다. 

아래는 제가 사용하는 fileprovider 방식으로 변경한 코드 입니다. 

  private void ShareAndroidImageWithText(string path, string title, string text, Texture2D _textureShareImage)
    {
#if UNITY_ANDROID
        AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
        AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent");
        {
            intentObject.Call<AndroidJavaObject>("setAction", intentClass.GetStatic<string>("ACTION_SEND"));
            intentObject.Call<AndroidJavaObject>("setType", "image/*");
            intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_SUBJECT"), title);
            intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TITLE"), title);
            intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), text);

            AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
            AndroidJavaObject unityContext = currentActivity.Call<AndroidJavaObject>("getApplicationContext");

            AndroidJavaClass uriClass = new AndroidJavaClass("android.support.v4.content.FileProvider");
            AndroidJavaClass fileClass = new AndroidJavaClass("java.io.File");

            string packageName = unityContext.Call<string>("getPackageName");
            string authority = packageName + ".fileprovider";
            AndroidJavaObject fileObject = new AndroidJavaObject("java.io.File", path);
            
            AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("getUriForFile", unityContext, authority, fileObject);

            bool fileExist = fileObject.Call<bool>("exists");
            Debug.Log("[GeneralShare] File exist : " + fileExist);
            Debug.Log("[GeneralShare] file path : " + uriObject.Call<string>("getPath"));
            
            // Attach image to intent
            if (fileExist)
                intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_STREAM"), uriObject);

            AndroidJavaObject jChooser = intentClass.CallStatic<AndroidJavaObject>("createChooser", intentObject, KW.Get("SYS_SNS_SHARE"));
            currentActivity.Call("startActivity", jChooser);
        }
#endif
    }

가운데 부분에 기존에 사용 하던 이런 형태의 코드에서 

            AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
            AndroidJavaClass fileClass = new AndroidJavaClass("java.io.File");

            AndroidJavaObject fileObject = new AndroidJavaObject("java.io.File", path);   
            AndroidJavaObject uriObject = uriClass.CallStatic("fromFile", fileObject);

 

이런식으로 변경합니다.

            AndroidJavaClass uriClass = new AndroidJavaClass("android.support.v4.content.FileProvider");
            AndroidJavaClass fileClass = new AndroidJavaClass("java.io.File");

            string packageName = unityContext.Call("getPackageName");
            string authority = packageName + ".fileprovider";
            AndroidJavaObject fileObject = new AndroidJavaObject("java.io.File", path);  

            AndroidJavaObject uriObject = uriClass.CallStatic("getUriForFile", unityContext, authority, fileObject);

 

코드를 수정했으면 

AndroidManifest.xml 파일에 fileprovider 를 사용할 수 있는 내용을 추가해야 합니다. 

  <application>
    <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="[패키지 네임].fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/filepaths" />
    </provider>
  </application>

내용 중 패키지 네임은 자신이 만든 프로젝트의 패키지 네임을 입력하시면 됩니다. 

패키지 네임은 com.xxxxx.xxxxx 이런식으로 되어 있죠.  

 

위 내용을 AndroidManifest.xml 추가 했으면 추가한 해당 AndroidManifest.xml 파일이 있는 곳에 

res 폴더를 만든 후(이미 있으면 그냥 사용해도 됩니다.) res 폴더 안에 xml 폴더를 만들고 filepaths.xml 파일을

만들고 아래의 코드를 입력합니다. 

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <external-path path="Android/data/com.company.product" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

이런식의 구조가 됩니다. 

위 경로 예시는 저는AndroidManifest.xml 에 들어가는 fileprovider 내용을 

구글 애드몹을 사용하기때문에 애드몹 플러그인에 같이 붙여 놓은 상태입니다.

 

꼭 이렇게 붙여놓을 필요는 없고 따로 AndroidManifest.xml 만들거나 다른 플러그인에 함께 사용해도 됩니다. 

 

구현 중 또는 후에 

android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference

 

이런 오류나 

NullPointerException when trying to get URI with FileProvider(Android)

 

이런식의 오류가 날 경우에는 AndroidManifest.xml 파일의 패키지 명이 정확하게 입력되어 있는지를 확인해보고 

xml 파일이 정확한 경로 위치에 있는지 확인합니다. 

res - xml - filepaths.xml