Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Native Share for Android & iOS [Open Source]

Discussion in 'Assets and Asset Store' started by yasirkula, Mar 1, 2018.

  1. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    I'd recommend updating to 2020.3.38 or newer since the issue linked in this post is fixed on that version.
     
  2. _uso212

    _uso212

    Joined:
    Jan 25, 2014
    Posts:
    9
    Hi @yasirkula first of all thanks for the plugin! I've been taking a look at the forum to see if something similar has been reported yet, but havent found it so I'll ask it. Do you have any type of workaround or fix planned for when the user perform the following steps?

    1. Bring up the native share.
    2. Select an app to share the content, which will bring up a new popup.
    3. Cancel the share on that app (i.e: Slack, iMessages, etc.) and go back to the app share selection (still in the native share).
    4. Select again the same or other app to share the text/link.
    5. Share it.

    What happens next is that when you do step 3, you get the
    Code (CSharp):
    1. NativeShare.ShareResult.NotShared
    and since you're not out of the native share yet, you are able to share it, but you don't get the appropriate callback (apparently since you only need to press "X"/"Close" once to get that callback).

    Do you have any workaround or planned fix for that? :D
     
  3. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    @_uso212 Hi! I wasn't aware that step 2 would still keep app share selection sheet active, I'm guessing this is on iOS. I guess we need to store the latest share result in a variable and call the callback function only when UIActivityViewController is closed. I wonder if the "UIActivityViewController-is-closed" check will return true in step 2 though because another view will be active at that time. This change requires making moderate amount of modifications to the codebase but I have neither a phone to test it on nor the time to invest in this fix at the moment :(
     
    _uso212 likes this.
  4. Developer1999-1

    Developer1999-1

    Joined:
    Oct 27, 2020
    Posts:
    32
    Hi! Thank you very much for creating this plugin. However, I do want to ask for some behavior on iOS. My game supports multi-language. However, it seems when for Photo Library Usage Description, since it is built into Info.plist, it is impossible to decide which language to show during runtime. So my question is:

    1. Is it possible to display different languages for this description in runtime?
    2. If not, what will happen if I simply leave the description blank? Would it cause trouble during App Review?

    Thank you very much!
     
  5. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    Hi! Leaving Photo Library Usage Description blank would crash the app if user clicks "Add to Photos" while sharing images. You can localize strings inside Info.plist but I have no experience with it. I'd recommend googling it.
     
  6. Developer1999-1

    Developer1999-1

    Joined:
    Oct 27, 2020
    Posts:
    32
    Gocha, thank you!
     
    yasirkula likes this.
  7. _uso212

    _uso212

    Joined:
    Jan 25, 2014
    Posts:
    9
    Thanks for the reply. I understand that this may requiere some time to get fixed. If you need some help with testing let me know and I'm glad to help :)
     
  8. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    Thank you! I wouldn't want you to keep your hopes high though because this is currently a very low priority for me and considering my ever growing backlog, I may not take a look at it in the near future.
     
  9. _uso212

    _uso212

    Joined:
    Jan 25, 2014
    Posts:
    9
    No problem. I understand! Is there a way to close the native plugin from the Unity code?
     
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    That'll require keeping a reference to UIActivityViewController in the native code and adding a function to force dismiss that view controller. On iPad, UIActivityViewController is displayed in a UIPopoverController so it might be necessary to dismiss that instead on those devices. As an implementation reference, NativeGallery stores references to some of its view controllers in static variables.

    PS. I wonder, does the share sheet not close automatically on both iPhone and iPad?
    PPS. Does the share sheet not close automatically on older versions of the plugin, as well: https://github.com/yasirkula/UnityNativeShare/releases/tag/v1.1.5
     
  11. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    @_uso212 Can you replace NativeShare.mm as follows and see if it works properly on your iPhone and iPad test devices when a content is shared or a share action is canceled? We didn't notice memory leaks but can you also check for memory leaks?
    Code (CSharp):
    1. #import <UIKit/UIKit.h>
    2. #import <Foundation/Foundation.h>
    3. #ifdef UNITY_4_0 || UNITY_5_0
    4. #import "iPhone_View.h"
    5. #else
    6. extern UIViewController* UnityGetGLViewController();
    7. #endif
    8.  
    9. #define CHECK_IOS_VERSION( version )  ([[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] != NSOrderedAscending)
    10.  
    11. // Credit: https://github.com/ChrisMaire/unity-native-sharing
    12.  
    13. // Credit: https://stackoverflow.com/a/29916845/2373034
    14. @interface UNativeShareEmailItemProvider : NSObject <UIActivityItemSource>
    15. @property (nonatomic, strong) NSString *subject;
    16. @property (nonatomic, strong) NSString *body;
    17. @end
    18.  
    19. // Credit: https://stackoverflow.com/a/29916845/2373034
    20. @implementation UNativeShareEmailItemProvider
    21. - (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
    22. {
    23.     return [self body];
    24. }
    25.  
    26. - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
    27. {
    28.     return [self body];
    29. }
    30.  
    31. - (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType
    32. {
    33.     return [self subject];
    34. }
    35. @end
    36.  
    37. extern "C" void _NativeShare_Share( const char* files[], int filesCount, const char* subject, const char* text, const char* link )
    38. {
    39.     NSMutableArray *items = [NSMutableArray new];
    40.    
    41.     // When there is a subject on iOS 7 or later, text is provided together with subject via a UNativeShareEmailItemProvider
    42.     // Credit: https://stackoverflow.com/a/29916845/2373034
    43.     if( strlen( subject ) > 0 && CHECK_IOS_VERSION( @"7.0" ) )
    44.     {
    45.         UNativeShareEmailItemProvider *emailItem = [UNativeShareEmailItemProvider new];
    46.         emailItem.subject = [NSString stringWithUTF8String:subject];
    47.         emailItem.body = [NSString stringWithUTF8String:text];
    48.        
    49.         [items addObject:emailItem];
    50.     }
    51.     else if( strlen( text ) > 0 )
    52.         [items addObject:[NSString stringWithUTF8String:text]];
    53.    
    54.     // Credit: https://forum.unity.com/threads/native-share-for-android-ios-open-source.519865/page-13#post-6942362
    55.     if( strlen( link ) > 0 )
    56.     {
    57.         NSString *urlRaw = [NSString stringWithUTF8String:link];
    58.         NSURL *url = [NSURL URLWithString:urlRaw];
    59.         if( url == nil )
    60.         {
    61.             // Try escaping the URL
    62.             if( CHECK_IOS_VERSION( @"9.0" ) )
    63.             {
    64.                 url = [NSURL URLWithString:[urlRaw stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]];
    65.                 if( url == nil )
    66.                     url = [NSURL URLWithString:[urlRaw stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]];
    67.             }
    68.             else
    69.                 url = [NSURL URLWithString:[urlRaw stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    70.         }
    71.        
    72.         if( url != nil )
    73.             [items addObject:url];
    74.         else
    75.             NSLog( @"Couldn't create a URL from link: %@", urlRaw );
    76.     }
    77.    
    78.     for( int i = 0; i < filesCount; i++ )
    79.     {
    80.         NSString *filePath = [NSString stringWithUTF8String:files[i]];
    81.         UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    82.         if( image != nil )
    83.             [items addObject:image];
    84.         else
    85.             [items addObject:[NSURL fileURLWithPath:filePath]];
    86.     }
    87.    
    88.     if( strlen( subject ) == 0 && [items count] == 0 )
    89.     {
    90.         NSLog( @"Share canceled because there is nothing to share..." );
    91.         UnitySendMessage( "NSShareResultCallbackiOS", "OnShareCompleted", "2" );
    92.        
    93.         return;
    94.     }
    95.    
    96.     UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
    97.     if( strlen( subject ) > 0 )
    98.         [activity setValue:[NSString stringWithUTF8String:subject] forKey:@"subject"];
    99.    
    100.     void (^shareResultCallback)(UIActivityType activityType, BOOL completed, UIActivityViewController *activityReference) = ^void( UIActivityType activityType, BOOL completed, UIActivityViewController *activityReference )
    101.     {
    102.         NSLog( @"Shared to %@ with result: %d", activityType, completed );
    103.        
    104.         if( activityReference )
    105.         {
    106.             const char *resultMessage = [[NSString stringWithFormat:@"%d%@", completed ? 1 : 2, activityType] UTF8String];
    107.             char *result = (char*) malloc( strlen( resultMessage ) + 1 );
    108.             strcpy( result, resultMessage );
    109.            
    110.             UnitySendMessage( "NSShareResultCallbackiOS", "OnShareCompleted", result );
    111.            
    112.             // On iPhones, the share sheet isn't dismissed automatically when share operation is canceled, do that manually here
    113.             if( !completed && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone )
    114.                 [activityReference dismissViewControllerAnimated:NO completion:nil];
    115.         }
    116.         else
    117.             NSLog( @"Share result callback is invoked multiple times!" );
    118.     };
    119.    
    120.     if( CHECK_IOS_VERSION( @"8.0" ) )
    121.     {
    122.         __block UIActivityViewController *activityReference = activity; // About __block usage: https://gist.github.com/HawkingOuYang/b2c9783c75f929b5580c
    123.         activity.completionWithItemsHandler = ^( UIActivityType activityType, BOOL completed, NSArray *returnedItems, NSError *activityError )
    124.         {
    125.             if( activityError != nil )
    126.                 NSLog( @"Share error: %@", activityError );
    127.            
    128.             shareResultCallback( activityType, completed, activityReference );
    129.             activityReference = nil;
    130.         };
    131.     }
    132.     else if( CHECK_IOS_VERSION( @"6.0" ) )
    133.     {
    134. #pragma clang diagnostic push
    135. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    136.         __block UIActivityViewController *activityReference = activity;
    137.         activity.completionHandler = ^( UIActivityType activityType, BOOL completed )
    138.         {
    139.             shareResultCallback( activityType, completed, activityReference );
    140.             activityReference = nil;
    141.         };
    142. #pragma clang diagnostic pop
    143.     }
    144.     else
    145.         UnitySendMessage( "NSShareResultCallbackiOS", "OnShareCompleted", "" );
    146.    
    147.     UIViewController *rootViewController = UnityGetGLViewController();
    148.     if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ) // iPhone
    149.     {
    150.         [rootViewController presentViewController:activity animated:YES completion:nil];
    151.     }
    152.     else // iPad
    153.     {
    154.         UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:activity];
    155.         [popup presentPopoverFromRect:CGRectMake( rootViewController.view.frame.size.width / 2, rootViewController.view.frame.size.height / 2, 1, 1 ) inView:rootViewController.view permittedArrowDirections:0 animated:YES];
    156.     }
    157. }
     
  12. sfjohansson

    sfjohansson

    Joined:
    Mar 12, 2013
    Posts:
    347
    Hi,
    Thanks for making this awesome plugin. I'm testing it with this bit of code and noticed that on some apps it reports back "NotShared", even if successful. For instance with discord but on facebook messenger it seems to work. The sharing view was also stuck after, although the code above dismissed it..but perhaps that's the wrong bug fix if the actual issue is the app that is being shared to?

    Code (CSharp):
    1.  var shareObject = new NativeShare()
    2.                 .AddFile(sourcePath)
    3.                 .SetCallback((result, shareTarget) =>
    4.                 {
    5.                     if (_shareResult.ContainsKey(sourcePath))
    6.                     {
    7.                         _shareResult[sourcePath] = result;
    8.                     }          
    9.                     else _shareResult.Add(sourcePath, result);
    10.                    
    11.                     Debug.Log($"ShareMedia Callback: {sourcePath} Result: {result}");
    12.                     done = true;
    13.                 });
    14.            
    15.             shareObject.Share();
     
  13. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    I've encountered NotShared once while sharing to Gmail on iOS but couldn't reproduce it with the same share content afterwards. Is this issue persistent on Discord?
     
  14. sfjohansson

    sfjohansson

    Joined:
    Mar 12, 2013
    Posts:
    347
    Yes, it was consistent with discord.
     
  15. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
  16. sfjohansson

    sfjohansson

    Joined:
    Mar 12, 2013
    Posts:
    347
    We started with that one and there were some other bugs with callbacks at the time, perhaps fixed now but the latest release requires iOS 14+.
     
  17. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    I mean if Discord is buggy on that plugin as well, then it's more likely an issue on Discord's end which they may fix in an update.
     
  18. sfjohansson

    sfjohansson

    Joined:
    Mar 12, 2013
    Posts:
    347
    Now that is interesting, as this was one of the reason that I looked at your plugin before we got the iOS11 requirement. Perhaps I never tried sharing to other apps, but I quickly noted down that there was issues with confirmation/callbacks... so it could potentially be discord
     
    yasirkula likes this.
  19. Jonnyharro

    Jonnyharro

    Joined:
    Dec 21, 2022
    Posts:
    1
    Sorry to ask this which I don't ask if you think then I am really sorry again

    I just want to integrate this Native Share feature with this (https://gbapps.net/gbwhatsapp-apk/) on my Android So I can easily share large file, So I would like to is it possible if so then can you please share guide me?

    thanks.
     
  20. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    Hi! You must have full source code access to the Android app to be able to add Native Share to it. If you're trying to integrate Native Share with an Android application found on the internet, unless the application is built with Unity and its source code is available, unfortunately you can't integrate Native Share to it.
     
  21. RemDust

    RemDust

    Joined:
    Aug 28, 2015
    Posts:
    409
    Hi there !
    Does the plugin also work on a desktop environment or only on android and ios mobile ?
     
  22. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    Hi! Only Android & iOS are supported.
     
  23. friuns3

    friuns3

    Joined:
    Oct 30, 2009
    Posts:
    307
    Hi is it possible to share 20mb file between unity apps on android with this plugin or some other way?
    I can't figure out how to receive it, I tried grant permission to a file but its not working, I'm getting access denied for reading file created by other app

    Code (CSharp):
    1. package com.example.myapplication
    2.  
    3. class MainActivity : Activity() {
    4.     @RequiresApi(Build.VERSION_CODES.KITKAT)
    5.     override fun onCreate(savedInstanceState: Bundle?) {
    6.         super.onCreate(savedInstanceState)
    7.  
    8.         // Write text to a file
    9.         val text = "Hello, World!"
    10.         val file = File(getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "/BrutalStrike/example2.glb")
    11.         try {
    12.             val stream = FileOutputStream(file)
    13.             stream.write(text.toByteArray())
    14.             stream.close()
    15.         } catch (e: IOException) {
    16.             e.printStackTrace()
    17.         }
    18.  
    19.         // Grant access to the file
    20.  
    21.         val fileUri = FileProvider.getUriForFile(this,  "com.example.myapplication.fileprovider", file)
    22.         val packageName = "a7.weapon.painter"
    23.  
    24.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    25.             applicationContext.grantUriPermission(
    26.                 packageName,
    27.                 fileUri,
    28.                 Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    29.             )
    30.         }
    31.     }
    32.  
    33.  
    34. }
     
  24. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    I understand what you're doing but NativeShare won't help here. My native knowledge also goes as far as FLAG_GRANT_READ_URI_PERMISSION. Since that didn't work, I unfortunately can't suggest other ideas :(
     
    friuns3 likes this.
  25. danidiazr

    danidiazr

    Joined:
    Dec 27, 2016
    Posts:
    23
    hello @yasirkula

    I'm having a problem sharing pictures with an issue regarding some FLAG_INMUTABLE on android API 31+. I've seen some others have asked about this.

    I just updated the package to the latest version today v1.4.9. When I'm call .commit(), my app crashes and I get the same error. I've managed to temporarily solve it by downgrading the maximum API level to android 11/API 30, with this done I don't get any errors but I can't publish on Google Play.

    Is there anything I can do?
     
  26. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    @danidiazr If you've updated via Package Manager, please download the latest release from the GitHub releases page instead (Package Manager's Asset Store is buggy on some versions) and in that case, be aware that your Unity version's Package Manager is buggy and it may not correctly update other assets on Asset Store either.
     
  27. danidiazr

    danidiazr

    Joined:
    Dec 27, 2016
    Posts:
    23
    @yasirkula I forgot to say that I updated with the package from Github. I was reading about this issue in this forum and I did update it from Github, still have the problem. Is this not happening? Is it just me?
     
  28. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620
    @danidiazr May I see the crash message as a reminder to myself?
     
  29. danidiazr

    danidiazr

    Joined:
    Dec 27, 2016
    Posts:
    23
    @yasirkula here it is. I couldn't get one on my side, on my devices it works perfectly cause I don't have an API 31+ device.
    upload_2023-1-30_15-46-56.png
     
  30. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,620