Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented type file #12

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ The setup requires a little bit more work. I will try to describe as detail as p

- Now go back to your extension file (in my case `MyShareEx.m`) and paste the following code there **being sure to substitute `MyShareEx` in all three places for whatever you chose above**

> If your project entry is `index.js` instead of `index.ios.js` then needs to replace `@"index.ios"` with `@"index"`

```objective-c
#import <Foundation/Foundation.h>
#import "ReactNativeShareExtension.h"
Expand Down Expand Up @@ -240,7 +242,7 @@ import java.util.List;
public class ShareApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;

}
Expand Down Expand Up @@ -543,6 +545,19 @@ export NODE_BINARY=node
../bin/react-native-xcode.sh
```

# Open container app
Steps needed to open the host application from the share extension.
1) Allow your app to be opened via URL Scheme - [Learn more](https://medium.com/react-native-training/deep-linking-your-react-native-app-d87c39a1ad5e)
2) In xcode, select share extension and go to Build Settings and set **Require Only App-Extension-Safe API** to `NO`.

Then you can open your app from the share extension by calling openURL:

```
import ShareExtension from 'react-native-share-extension';

ShareExtension.openURL('sample://example/url');
```

# Troubleshooting on iOS devices

Using the iOS Simulator and remote react-native debugger to develop the extension can hide issues that won't occur until testing on device. If you're experiencing issues running the extension on iOS devices, examine the Xcode console or device log for any obvious errors. If the Xcode console isn't receiving console output, ensure that the OS_ACTIVITY_MODE=disable environment var isn't enabled for the active scheme (see https://github.com/facebook/react-native/issues/10027). OS_ACTIVITY_MODE will hide device logging in the Xcode console, so its use is only advisable for iOS Simulator. For release mode, in order to view console output and see all output in the syslog, uncomment the `RCTSetLogThreshold(RCTLogLevelInfo - 1);` statement in your MyShareEx class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
import android.content.ContentUris;
import android.os.Environment;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

public class RealPathUtil {
public static String getRealPathFromURI(final Context context, final Uri uri) {

Expand Down Expand Up @@ -62,12 +69,7 @@ else if (isMediaDocument(uri)) {
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {

// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();

return getDataColumn(context, uri, null, null);
return getImagePath(context, uri);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
Expand Down Expand Up @@ -135,12 +137,77 @@ public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}

public static String getImagePath(Context context, Uri uri){
if ("content".equalsIgnoreCase(uri.getScheme())) {

if (isGoogleOldPhotosUri(uri)) {
// return http path, then download file.
return uri.getLastPathSegment();
} else if (isGoogleNewPhotosUri(uri) || isMMSFile(uri)) {
// copy from uri. context.getContentResolver().openInputStream(uri);
return copyFile(context, uri);
}
}

return getDataColumn(context, uri, null, null);
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
public static boolean isGoogleOldPhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

public static boolean isGoogleNewPhotosUri(Uri uri) {
return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
}

public static boolean isMMSFile(Uri uri) {
return "com.android.mms.file".equals(uri.getAuthority());
}

private static String copyFile(Context context, Uri uri) {

String filePath;
InputStream inputStream = null;
BufferedOutputStream outStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);

File extDir = context.getExternalFilesDir(null);
filePath = extDir.getAbsolutePath() + "/IMG_" + UUID.randomUUID().toString() + ".jpg";
outStream = new BufferedOutputStream(new FileOutputStream
(filePath));

byte[] buf = new byte[2048];
int len;
while ((len = inputStream.read(buf)) > 0) {
outStream.write(buf, 0, len);
}

} catch (IOException e) {
e.printStackTrace();
filePath = "";
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (outStream != null) {
outStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

return filePath;
}

}
38 changes: 11 additions & 27 deletions ios/ReactNativeShareExtension.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ - (void)viewDidLoad {



RCT_EXPORT_METHOD(openURL:(NSString *)url) {
UIApplication *application = [UIApplication sharedApplication];
NSURL *urlToOpen = [NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[application openURL:urlToOpen options:@{} completionHandler: nil];
}



RCT_REMAP_METHOD(data,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
Expand All @@ -60,7 +68,6 @@ - (void)viewDidLoad {
}

- (void)extractDataFromContext:(NSExtensionContext *)context withCallback:(void(^)(NSString *value, NSString* contentType, NSException *exception))callback {

@try {
NSExtensionItem *item = [context.inputItems firstObject];
NSArray *attachments = item.attachments;
Expand Down Expand Up @@ -92,31 +99,10 @@ - (void)extractDataFromContext:(NSExtensionContext *)context withCallback:(void(
}];
} else if (imageProvider) {
[imageProvider loadItemForTypeIdentifier:IMAGE_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {

/**
* Save the image to NSTemporaryDirectory(), which cleans itself tri-daily.
* This is necessary as the iOS 11 screenshot editor gives us a UIImage, while
* sharing from Photos and similar apps gives us a URL
* Therefore the solution is to save a UIImage, either way, and return the local path to that temp UIImage
* This path will be sent to React Native and can be processed and accessed RN side.
**/

UIImage *sharedImage;
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"RNSE_TEMP_IMG"];
NSString *fullPath = [filePath stringByAppendingPathExtension:@"png"];

if ([(NSObject *)item isKindOfClass:[UIImage class]]){
sharedImage = (UIImage *)item;
}else if ([(NSObject *)item isKindOfClass:[NSURL class]]){
NSURL* url = (NSURL *)item;
NSData *data = [NSData dataWithContentsOfURL:url];
sharedImage = [UIImage imageWithData:data];
}

[UIImagePNGRepresentation(sharedImage) writeToFile:fullPath atomically:YES];

NSURL *url = (NSURL *)item;

if(callback) {
callback(fullPath, [fullPath pathExtension], nil);
callback([url absoluteString], [[[url absoluteString] pathExtension] lowercaseString], nil);
}
}];
} else if (textProvider) {
Expand All @@ -140,6 +126,4 @@ - (void)extractDataFromContext:(NSExtensionContext *)context withCallback:(void(
}
}



@end
21 changes: 21 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Type definitions for [react-native-share-extension]
// Project: https://github.com/padlet/react-native-share-extension
declare module 'react-native-share-extension' {
interface RNShareData {
value: string;
type: "text/url" | "images/*" | "text/plain";
}
interface RNShareFiles {
files: RNShareData[]
}

interface ShareExtension {
close(): void;
data(): Promise<RNShareFiles>;
openURL(uri: string): void;
}

const RNShareExtension: ShareExtension;
export default RNShareExtension;
export type { RNShareData };
}
3 changes: 2 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import { NativeModules } from 'react-native'
// NativeModules.ShareExtension.close()
export default {
data: () => NativeModules.ReactNativeShareExtension.data(),
close: () => NativeModules.ReactNativeShareExtension.close()
close: () => NativeModules.ReactNativeShareExtension.close(),
openURL: (url) => NativeModules.ReactNativeShareExtension.openURL(url),
}
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
{
"name": "react-native-share-extension",
"version": "1.2.1",
"version": "2.0.0",
"description": "share extension using react-native for both ios and android",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"android",
"ios",
"lib/"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down