21

WebView in questa applicazione apre una pagina con pulsante di caricamento.Carica un'immagine dalla fotocamera o galleria in WebView

Page in webview with upload button

Di seguito si riporta il blocco di codice che permette di aprire una finestra di dialogo per caricare un'immagine dalla galleria o fotocamera.

Nella mia attività ho:

private WebView wv; 

//make HTML upload button work in Webview 
private ValueCallback<Uri> mUploadMessage; 
private final static int FILECHOOSER_RESULTCODE=1; 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 
    if(requestCode==FILECHOOSER_RESULTCODE) 
    { 
    if (null == mUploadMessage) return; 
      Uri result = intent == null || resultCode != RESULT_OK ? null 
        : intent.getData(); 
      mUploadMessage.onReceiveValue(result); 
      mUploadMessage = null;   
    } 
} 

Entro onCreate Ho il seguente:

wv.setWebChromeClient(new WebChromeClient() { 
     private Uri imageUri; 

     public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {  
      File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp"); 
      // Create the storage directory if it does not exist 
      if (! imageStorageDir.exists()){ 
       imageStorageDir.mkdirs();     
      } 
      File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); 
      imageUri = Uri.fromFile(file); 

      final List<Intent> cameraIntents = new ArrayList<Intent>(); 
      final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
      final PackageManager packageManager = getPackageManager(); 
      final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0); 
      for(ResolveInfo res : listCam) { 
       final String packageName = res.activityInfo.packageName; 
       final Intent i = new Intent(captureIntent); 
       i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name)); 
       i.setPackage(packageName); 
       i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 
       cameraIntents.add(i); 

      } 


      mUploadMessage = uploadMsg; 
      Intent i = new Intent(Intent.ACTION_GET_CONTENT); 
      i.addCategory(Intent.CATEGORY_OPENABLE); 
      i.setType("image/*"); 
      Intent chooserIntent = Intent.createChooser(i,"Image Chooser"); 
      chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{})); 
      MainActivity.this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); 
     } 

sono in grado di vedere l'opzione per la macchina fotografica, galleria di immagini e file explorer su facendo clic sul pulsante Carica. Camera, gallery and file explorer upload option

Esplora file e Galleria funziona come previsto. Il problema è che, quando scatto una foto utilizzando la fotocamera, non viene caricata nell'opzione "scegli file" che mostra lo stato "Nessun file scelto".

FOTOCAMERA SELEZIONE:

camera

ON si scattano delle foto utilizzando la fotocamera: indietro e controllare le opzioni appaiono.

snapshot using camera

sulla scelta SPUNTA:

file non viene inserita per ora :(IN "Scegli file" OPZIONE

enter image description here

cosa ci si aspetta:

image uploaded

ho controllato che ho il permesso di scrittura corretta e, quindi, una directory chiamata "MyApp" viene generato e l'immagine viene memorizzata all'interno di esso (se preso invocando telecamera dopo aver cliccato sul pulsante Carica pagina web).

Come dire programmaticamente all'applicazione di scegliere la foto scattata dalla fotocamera (che è stata memorizzata nella directory MyApp) dopo aver toccato il segno di spunta?

+0

Viene chiamato 'onActivityResult'? Quali sono i valori di 'requestCode' e' Uri result'? – vorrtex

+0

onActivityResult non viene chiamato ... – Chirag

+0

https://gist.github.com/chirag-v/5281337 Inoltre, sono target API 8 in poi. Per qualche motivo il codice sopra non funzionava sul mio desiderio HTC C (Android 4.0.3). Così, sono tornato alla versione precedente senza opzione per la fotocamera. :( – Chirag

risposta

10

Suppongo che il metodo onActivityResult sia effettivamente chiamato, ma il 3 ° parametro Intent intent è nullo. Sembra che sia un bug dei telefoni Nexus.

Ma è possibile salvare l'immagine di output uri alla variabile privata e utilizzarlo al posto del intento:

private Uri imageUri; 

private void showAttachmentDialog(ValueCallback<Uri> uploadMsg) { 
    this.mUploadMessage = uploadMsg; 

    File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "TestApp"); 
    if (!imageStorageDir.exists()) { 
     imageStorageDir.mkdirs(); 
    } 
    File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); 
    this.imageUri = Uri.fromFile(file); // save to the private variable 

    final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
    captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 

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

    Intent chooserIntent = Intent.createChooser(i, "Image Chooser"); 
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] { captureIntent }); 

    this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); 
} 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 
    if (requestCode == FILECHOOSER_RESULTCODE) { 
     if (null == this.mUploadMessage) { 
      return; 
     } 

     Uri result; 
     if (resultCode != RESULT_OK) { 
      result = null; 
     } else { 
      result = intent == null ? this.imageUri : intent.getData(); // retrieve from the private variable if the intent is null 
     } 

     this.mUploadMessage.onReceiveValue(result); 
     this.mUploadMessage = null; 
    } 
} 

In questo codice ho aggiunto la variabile imageUri per l'attività e usato in entrambi i metodi.

+0

@Chirag Of couse non funzionerà. Dov'è il tuo metodo 'openFileChooser'? – vorrtex

+0

Con questo dà la finestra di dialogo Scelta file ===> https://gist.github.com/chirag-v/5281599 ma manca l'opzione per la fotocamera, (solo file explorer e galleria di immagini). Vedi la linea 155 in poi .. – Chirag

+0

@Chirag Che cosa hai fatto lì? Non hai usato il mio metodo 'showAttachmentDialog'. – vorrtex

2

assicurarsi che non si dispone di android:launchMode="singleInstance" nel file manifesto

+0

Cool, ma perché no? Si prega di elaborare la risposta. –

6

Aggiornamento 6/18: Questo non sembra di lavorare su Samsung Galaxy S2 con Android 4.2.1. Questo ha funzionato bene su HTC One X + con 4.1.2. Essere ammonito


Ho affrontato lo stesso problema. Ecco il problema e come l'ho risolto.

Problema:

Quando openFileChooser viene chiamato, la chiamata oggetto ValueCallback<Uri> viene passato. Questa è la chiamata effettiva alla visualizzazione web quando abbiamo un file pronto per questo. Salviamo questo oggetto a mUploadMessage e utilizziamo la funzione mUploadMessage.onReceiveValue() in onActivityResult per restituire il file a Webview. Mentre scegli la fotocamera, fai clic su un'immagine, salvala e ritorna all'attività webview, la nostra attività potrebbe essere riciclata, ovvero, il che significa che effettivamente perdiamo l'oggetto di richiamata mUploadMessage. E quindi, il file non può essere restituito a Webview per il caricamento.

Fix:

Fix comporta l'esecuzione di alcuni javascript, così abilitare javascript WebView. Fondamentalmente, otterremo un altro oggetto di richiamo se abbiamo perso il precedente.

Abbiamo bisogno di creare un campo booleano 'mUploadFileOnLoad' e tre campi.

private int mReturnCode; 
    private int mResultCode; 
    private Intent mResultIntent; 
    private boolean mUploadFileOnLoad = false; 

Quando torneremo alla nostra attività dalla macchina fotografica, sarà chiamato onActivityResult. Se l'attività viene ricostruita, mUploadMessage è nullo. Quindi, salveremo i parametri nei campi e imposteremo mUploadFileOnLoad su true e restituiremo. L'altra parte è molto importante.

@Override 
    protected void onActivityResult(int requestCode, 
            int resultCode, 
            Intent intent) 
    { 
     //if the callback object has been recycled  
     if(null==mUploadMessage) 
     { 
     //Save the result 
     mReturnCode = requestCode; 
     mResultCode = resultCode; 
     mResultIntent = intent; 
     //remember to invoke file upload using Javascript 
     mUploadFileOnLoad = true; 
     return; 
     }else 
     mUploadFileOnLoad = false; 
     //rest of the code 
    } 

Le parti importanti di questa soluzione sono in WebViewClient e WebChromeClient

new WebChromeClient() { 

     //Other overloaded functions 

     //See http://stackoverflow.com/a/15423907/375093 for full explanation 
     //The undocumented magic method override 
     //Eclipse will swear at you if you try to put @Override here 
     // For Android < 3.0 
     public void openFileChooser(ValueCallback<Uri> uploadMsg) { 
      //If we lost the callback object 
      if(mUploadFileOnLoad) 
      { 
       mUploadMessage = uploadMsg; 
       //use the saved result objects to invoke onActivityResult 
       onActivityResult(mReturnCode, mResultCode, mResultIntent); 
       return; 
      } 
     //Rest of the code.... 
     } 

e

 new WebViewClient() { 
     @Override 
     public void onPageFinished(WebView view, String url) { 
      if(mUploadFileOnLoad) 
      { 
       webview.loadUrl("javascript:document.getElementById('my_file').click()"); 
      } 
     } 

In precedenza, my_file è l'id dell'elemento <input> nella pagina web.

<input type="file" id="my_file"> 

Così, in sintesi, quello che abbiamo fatto è - quando non abbiamo un oggetto callback, abbiamo salvare i dati ricevuti da altre attività e impostare mUploadFileOnLoad true e attendere per la pagina da caricare. Quando la pagina viene caricata, usiamo Javascript per invocare il selettore di file in modo da ottenere un oggetto di callback. Poiché abbiamo già dei risultati, invochiamo onActivityResult e restituiamo. onActivityResult ora ha un callback dalla webview.

+0

grazie per aver condiviso questo. Ho implementato la tua soluzione. Ma in onActivityResult mi restituisce sempre -1 in entrambe le condizioni im prendere immagine tramite fotocamera o selezionare img dalla galleria –

+0

@QadirHussain -1 significa che l'operazione è riuscita, cioè, l'attività della fotocamera ha catturato l'immagine o la galleria ha selezionato il immagine. Vedi http://developer.android.com/reference/android/app/Activity.html#RESULT_OK. Per questa soluzione, è necessario verificare se l'oggetto callback diventa null e quindi richiamare onActivityResult. Aggiornamento del codice precedente per maggiore chiarezza – Sundeep

2

Mi dispiace il mio inglese.

Questa è la soluzione.

Il primo, si definisce membro del file in questo modo.

public File mTempFile;

vostra di openFileChooser è ok.

onActivityResult metodo è così importante.

app fotocamera non restituire url, ma ValueCallback deve avere url.

Ottieni uri da mTempFile.

questo è lavoro.

Io uso così.

if (mTempFile.exists()) { 

    mUploadMessage.onReceiveValue(Uri.fromFile(mTempFile)); 
    mUploadMessage = null; 

} else { 

    mUploadMessage.onReceiveValue(result); 
    mUploadMessage = null; 
} 

Se esiste mTempFile chiamato fotocamera, altro caso dalla galleria.

4

Dopo aver lottato un sacco ho trovato un codice che funziona per l'adozione di file da cucina e camera da 5.0+ dispositivi

private ValueCallback<Uri> mUploadMessage; 
private Uri mCapturedImageURI = null; 
private ValueCallback<Uri[]> mFilePathCallback; 
private String mCameraPhotoPath; 
private static final int INPUT_FILE_REQUEST_CODE = 1; 
private static final int FILECHOOSER_RESULTCODE = 1; 



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 = File.createTempFile(
      imageFileName, /* prefix */ 
      ".jpg",   /* suffix */ 
      storageDir  /* directory */ 
    ); 
    return imageFile; 
} 

questo è l'inizializzazione e l'impostazione WebView

 mWebView= (WebView) findViewById(R.id.webview); 
     mWebView.getSettings().setJavaScriptEnabled(true); 
     mWebView.getSettings().setPluginState(WebSettings.PluginState.OFF); 
     mWebView.getSettings().setLoadWithOverviewMode(true); 
     mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); 
     mWebView.getSettings().setUseWideViewPort(true); 
     mWebView.getSettings().setUserAgentString("Android Mozilla/5.0 AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"); 
     mWebView.getSettings().setAllowFileAccess(true); 
     mWebView.getSettings().setAllowFileAccess(true); 
     mWebView.getSettings().setAllowContentAccess(true); 
     mWebView.getSettings().supportZoom(); 
     mWebView.loadUrl(Common.adPostUrl); 

     mWebView.setWebViewClient(new WebViewClient() { 
      public boolean shouldOverrideUrlLoading(WebView view, String url) { 
       // do your handling codes here, which url is the requested url 
       // probably you need to open that url rather than redirect: 
       if (url.contains(".pdf")){ 
        Intent intent = new Intent(Intent.ACTION_VIEW); 
        intent.setDataAndType(Uri.parse(url), "application/pdf"); 
        try{ 
         view.getContext().startActivity(intent); 
        } catch (ActivityNotFoundException e) { 
         //user does not have a pdf viewer installed 
        } 
       } else { 
        mWebView.loadUrl(url); 
       } 
       return false; // then it is not handled by default action 
      } 


      @Override 
      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { 

Log.e("error",description); 
      } 


      @Override 
      public void onPageStarted(WebView view, String url, Bitmap favicon) {  //show progressbar here 

       super.onPageStarted(view, url, favicon); 
      } 

      @Override 
      public void onPageFinished(WebView view, String url) { 
      //hide progressbar here 

      } 

     }); 
     mWebView.setWebChromeClient(new ChromeClient()); 

Ed ecco il mio ChomeClient() metodo

public class ChromeClient extends WebChromeClient { 

    // For Android 5.0 
    public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) { 
     // Double check that we don't have any existing callbacks 
     if (mFilePathCallback != null) { 
      mFilePathCallback.onReceiveValue(null); 
     } 
     mFilePathCallback = filePath; 

     Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
     if (takePictureIntent.resolveActivity(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 
       Log.e(Common.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); 

     startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE); 

     return true; 

    } 

    // openFileChooser for Android 3.0+ 
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { 

     mUploadMessage = uploadMsg; 
     // Create AndroidExampleFolder at sdcard 
     // Create AndroidExampleFolder at sdcard 

     File imageStorageDir = new File(
       Environment.getExternalStoragePublicDirectory(
         Environment.DIRECTORY_PICTURES) 
       , "AndroidExampleFolder"); 

     if (!imageStorageDir.exists()) { 
      // Create AndroidExampleFolder at sdcard 
      imageStorageDir.mkdirs(); 
     } 

     // Create camera captured image file path and name 
     File file = new File(
       imageStorageDir + File.separator + "IMG_" 
         + String.valueOf(System.currentTimeMillis()) 
         + ".jpg"); 

     mCapturedImageURI = Uri.fromFile(file); 

     // Camera capture image intent 
     final Intent captureIntent = new Intent(
       android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 

     captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI); 

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

     // Create file chooser intent 
     Intent chooserIntent = Intent.createChooser(i, "Image Chooser"); 

     // Set camera intent to file chooser 
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS 
       , new Parcelable[] { captureIntent }); 

     // On select image call onActivityResult method of activity 
     startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); 


    } 

    // openFileChooser for Android < 3.0 
    public void openFileChooser(ValueCallback<Uri> uploadMsg) { 
     openFileChooser(uploadMsg, ""); 
    } 

    //openFileChooser for other Android versions 
    public void openFileChooser(ValueCallback<Uri> uploadMsg, 
           String acceptType, 
           String capture) { 

     openFileChooser(uploadMsg, acceptType); 
    } 

} 

// ecco il mio metodo onActivityResult da gestire e dati dalla galleria o fotocamera intento

@Override 
public void onActivityResult(int requestCode, int resultCode, Intent data) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 

     if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) { 
      super.onActivityResult(requestCode, resultCode, data); 
      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)}; 
       } 
      } 
     } 

     mFilePathCallback.onReceiveValue(results); 
     mFilePathCallback = null; 

    } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 
     if (requestCode != FILECHOOSER_RESULTCODE || mUploadMessage == null) { 
      super.onActivityResult(requestCode, resultCode, data); 
      return; 
     } 

     if (requestCode == FILECHOOSER_RESULTCODE) { 

      if (null == this.mUploadMessage) { 
       return; 

      } 

      Uri result = null; 

      try { 
       if (resultCode != RESULT_OK) { 

        result = null; 

       } else { 

        // retrieve from the private variable if the intent is null 
        result = data == null ? mCapturedImageURI : data.getData(); 
       } 
      } catch (Exception e) { 
       Toast.makeText(getApplicationContext(), "activity :" + e, 
         Toast.LENGTH_LONG).show(); 
      } 

      mUploadMessage.onReceiveValue(result); 
      mUploadMessage = null; 

     } 
    } 

    return; 
} 

ed ecco le autorizzazioni necessarie per aprire fotocamera

<uses-permission android:name="android.permission.CAMERA" /> 
<uses-permission android:name="android.permission.CAMERA2" /> // for new versions api 21+ 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 

Nota: Il mio codice contiene anche il codice per i dispositivi che eseguono 3.0 + anche, ma non li ho mai testato, il codice precedente funzionava con gli emulatori Lolipop, Marshmallow e Nougat. Un'altra cosa, se vedi l'icona di Android System al posto di Camera significa che hai molte app disponibili nel tuo dispositivo per gestire la fotocamera.

+0

awsm it owrks :) –

Problemi correlati