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

Add file input to WebView #12807

Closed
wants to merge 5 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.app.Activity;
import android.content.Context;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.LifecycleEventListener;
Expand Down Expand Up @@ -48,6 +49,16 @@ public void removeLifecycleEventListener(LifecycleEventListener listener) {
mReactApplicationContext.removeLifecycleEventListener(listener);
}

@Override
public void addActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.addActivityEventListener(listener);
}

@Override
public void removeActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.removeActivityEventListener(listener);
}

@Override
public boolean hasCurrentActivity() {
return mReactApplicationContext.hasCurrentActivity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@

import javax.annotation.Nullable;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.ViewGroup.LayoutParams;
import android.webkit.ConsoleMessage;
Expand All @@ -34,6 +41,7 @@
import android.webkit.WebSettings;

import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
Expand Down Expand Up @@ -103,8 +111,13 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
// state and release page resources (including any running JavaScript).
private static final String BLANK_URL = "about:blank";

public static final int INPUT_FILE_REQUEST_CODE = 1001;
public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";

private WebViewConfig mWebViewConfig;
private @Nullable WebView.PictureListener mPictureListener;
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;

protected static class ReactWebViewClient extends WebViewClient {

Expand Down Expand Up @@ -330,7 +343,7 @@ public String getName() {
}

@Override
protected WebView createViewInstance(ThemedReactContext reactContext) {
protected WebView createViewInstance(final ThemedReactContext reactContext) {
ReactWebView webView = new ReactWebView(reactContext);
webView.setWebChromeClient(new WebChromeClient() {
@Override
Expand All @@ -346,8 +359,110 @@ public boolean onConsoleMessage(ConsoleMessage message) {
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}

private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File imageFile = new File(
storageDir, /* directory */
imageFileName+".jpg" /* filename */
);
return imageFile;
}

public boolean onShowFileChooser(
WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
if(mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(reactContext.getCurrentActivity().getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
FLog.e(ReactConstants.TAG, "Unable to create Image File", ex);
}

// Continue only if the File was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}

Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");

Intent[] intentArray;
if(takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}

Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

reactContext.getCurrentActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

return true;
}
});

reactContext.addLifecycleEventListener(webView);
reactContext.addActivityEventListener(new ActivityEventListener() {
@Override
public void onActivityResult (Activity activity, int requestCode, int resultCode, Intent data) {
if(requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) {
return;
}
Uri[] results = null;

// Check that the response is a good one
if(resultCode == Activity.RESULT_OK) {
if(data == null) {
// If there is not data, then we may have taken a photo
if(mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}

if(results == null) {
mFilePathCallback.onReceiveValue(new Uri[]{});
}
else {
mFilePathCallback.onReceiveValue(results);
}
mFilePathCallback = null;
return;
}

@Override
public void onNewIntent(Intent intent) {}
});
mWebViewConfig.configWebView(webView);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(false);
Expand Down