my name is Daniel and I'm learning Android App Dev.
With the code below its possible to attach a file selecting it via file browsing, but what I also need is to call the native android camera app to take a instant photo with the camera and the provide the Upload Form the correct path to the file.
During some testes I could log.v the path to the file, which showed a valid path, since got there and could see the created photo.
I've tried several solutions around the web and couldn't find anything specific for API 25 and above, what seems to be the cause of the problem.
package br.com.fahrenheitdlp.ouvidoria;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private WebView webView;
private static final String TAG = MainActivity.class.getSimpleName();
private String mCM;
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
private final static int FCR = 1;
private final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
@SuppressLint({"SetJavaScriptEnabled", "WrongViewCast"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
// Chamando o teste de permissões
checkingForPermissions();
// Ajustando o WebView
webView = (WebView) findViewById(R.id.webView1);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setUseWideViewPort(true);
webSettings.setDomStorageEnabled(true);
if (Build.VERSION.SDK_INT >= 21) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}else if (Build.VERSION.SDK_INT >= 19) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}else if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 19) {
webView.requestFocus();
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webView.setVerticalScrollBarEnabled(false);
webView.setWebViewClient(new Callback());
// Carrega a página da ouvidoria
webView.loadUrl("http://suporte.mesquita.rj.gov.br/ouvidoriaweb/");
// webView.loadUrl("http://files.fm");
// Configura o upload de arquivos apartir do armazenamento externo ou câmera
webView.setWebChromeClient(new WebChromeClient(){
//For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg){
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i,"Escolha um arquivo"), FCR);
}
// For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this
public void openFileChooser(ValueCallback uploadMsg, String acceptType){
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, "File Browser"), FCR);
}
//For Android 4.1+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
}
//For Android 5.0+
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams){
if (mUMA != null){
mUMA.onReceiveValue(null);
}
mUMA = filePathCallback;
Intent takePictureIntent = new Intent();
takePictureIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
if(takePictureIntent.resolveActivity(getPackageManager()) != null){
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
String authorities = getApplicationContext().getPackageName() + ".fileprovider";
Uri imageUri = FileProvider.getUriForFile(MainActivity.this, authorities, photoFile);
Log.v(TAG, imageUri.toString());
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
Log.d(TAG, "TakePicture não é NULL");
intentArray = new Intent[]{takePictureIntent};
} else {
Log.d(TAG, "TakePicture é NULL");
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Escolha um arquivo");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
return true;
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
if (requestCode != FCR || mUMA == null) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null || data.getDataString() == null) {
// If there is not data, then we may have taken a photo
if (mCM != null) {
results = new Uri[]{Uri.parse(mCM)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mUMA.onReceiveValue(results);
mUMA = null;
} else {
if (requestCode == FCR) {
if (null == mUM) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
mUM.onReceiveValue(result);
mUM = null;
}
}
// ScanFile so it will be appeared on Gallery
Uri imageUri = Uri.parse(mCM);
MediaScannerConnection.scanFile(MainActivity.this,
new String[]{imageUri.getPath()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
}
});
}
// Criando um arquivo de imagem
private File createImageFile() throws IOException{
@SuppressLint("SimpleDateFormat")
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date());
String imageFileName = "OW_"+timeStamp+"_";
//File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File storageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), "Camera");
File imageFile = File.createTempFile(
imageFileName,
".jpg",
storageDir
);
mCM = imageFile.getAbsolutePath();
return imageFile;
}
// Função para checagem de permissões
private void checkingForPermissions() {
if (Build.VERSION.SDK_INT < 23 && Build.VERSION.SDK_INT >= 16) {
int hasReadExtPermission = ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
int hasCameraPermission = ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CAMERA);
if (hasReadExtPermission != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showMessageOKCancel("You need to allow access to Contacts",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return;
}
}
if (hasCameraPermission != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.CAMERA)) {
showMessageOKCancel("You need to allow access to Contacts",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
}
}
} else {
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionsNeeded.add("Cartão de Memória");
if (!addPermission(permissionsList, Manifest.permission.CAMERA))
permissionsNeeded.add("Câmera");
if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Será necessário usar Rationale
String message = "Para anexar arquivos, por favor conceda acesso ao(à): " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return;
}
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
}
}
// Função para o agrupamento das permissões que não foram cedidas
private boolean addPermission(List<String> permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Checa o uso do Rationale
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
{
Map<String, Integer> perms = new HashMap<String, Integer>();
// Inicializando o HashMap perms
perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
// Preenchendo com os resultados
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Determinando as permissões já cedidas
if (perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|| perms.get(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Alguma das permissões foi negada em algum momento
Toast.makeText(MainActivity.this, "Alguma permissão foi negada", Toast.LENGTH_SHORT)
.show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
// Cuidado do alerta de permissão
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("Permitir", okListener)
.setNegativeButton("Negar", null)
.create()
.show();
}
// Callback do WebView
private class Callback extends WebViewClient {
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
Toast.makeText(getApplicationContext(), "Failed loading app!", Toast.LENGTH_SHORT).show();
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
Toast.makeText(getApplicationContext(), "Something Went Wrong!", Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN){
switch(keyCode){
case KeyEvent.KEYCODE_BACK:
if(webView.canGoBack()){
webView.goBack();
}else{
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
}
This is my Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.gov.rj.mesquita.ouvidoriaweb">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk tools:overrideLibrary="android.support.test.uiautomator.v18"/>
<uses-feature android:name="android.hardware.camera"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Ouvidoria Web"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".App"
tools:ignore="AllowBackup" >
<activity
android:name=".SplashActivity"
android:label="Ouvidoria Web"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
<provider
android:authorities="${applicationId}.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
Thanks in advance for everyone!
Best regards, Daniel.