【Android】WebView的input上傳照片的兼容問題

問題

前幾天接到的一個需求莺掠,是關(guān)于第三方理財產(chǎn)品的H5上傳照片問題。
對方說他們的新的需求牍汹,需要接入方配合上傳資產(chǎn)照片的需求,測試之后發(fā)現(xiàn)我們這邊的app端柬泽,IOS端上傳沒有問題慎菲,而Android端則點擊沒有任何反應(yīng)。
對方H5調(diào)用的方式是通過<input type='file' accept='image/*'/>的方式調(diào)用锨并,本來以為這個問題很簡單露该,就是app端沒有設(shè)置相機權(quán)限,造成的點擊無反應(yīng)情況第煮,而實際上加了之后發(fā)現(xiàn)解幼,并非簡單的權(quán)限問題抑党。

解決問題

因為Android的版本碎片問題,很多版本的WebView都對喚起函數(shù)有不同的支持撵摆。
我們需要重寫WebChromeClient下的openFileChooser()(5.0及以上系統(tǒng)回調(diào)onShowFileChooser())底靠。我們通過Intent在openFileChooser()中喚起系統(tǒng)相機和支持Intent的相關(guān)app。
在系統(tǒng)相機或者相關(guān)app中一頓操作之后特铝,當返回app的時候暑中,我們在onActivityResult()中將選擇好的圖片通過ValueCallbackonReceiveValue方法返回給WebView。

附上代碼:

  1. 首先是重寫各個版本的WebChromeClient的支持

    webView.setWebChromeClient(new WebChromeClient() {
      //For Android 3.0+
      public void openFileChooser(ValueCallback<Uri> uploadMsg) {
          selectImage();
          mUM = uploadMsg;
          Intent i = new Intent(Intent.ACTION_GET_CONTENT);
          i.addCategory(Intent.CATEGORY_OPENABLE);
          i.setType("*/*");
          MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), 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) {
          selectImage();
          mUM = uploadMsg;
          Intent i = new Intent(Intent.ACTION_GET_CONTENT);
          i.addCategory(Intent.CATEGORY_OPENABLE);
          i.setType("*/*");
          MyBaseWebViewActivity.this.startActivityForResult(
                  Intent.createChooser(i, "File Browser"),
                  FCR);
      }
    
      //For Android 4.1+
      public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
          selectImage();
          mUM = uploadMsg;
          Intent i = new Intent(Intent.ACTION_GET_CONTENT);
          i.addCategory(Intent.CATEGORY_OPENABLE);
          i.setType("*/*");
          MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MyBaseWebViewActivity.FCR);
      }
    
      //For Android 5.0+
      public boolean onShowFileChooser(
              WebView webView, ValueCallback<Uri[]> filePathCallback,
              WebChromeClient.FileChooserParams fileChooserParams) {
          selectImage();
          if (mUMA != null) {
              mUMA.onReceiveValue(null);
          }
          mUMA = filePathCallback;
          Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
          if (takePictureIntent.resolveActivity(MyBaseWebViewActivity.this.getPackageManager()) != null) {
              File photoFile = null;
              try {
                  photoFile = createImageFile();
                  takePictureIntent.putExtra("PhotoPath", mCM);
              } catch (IOException ex) {
                  Log.e(TAG, "Image file creation failed", ex);
              }
              if (photoFile != null) {
                  mCM = "file:" + photoFile.getAbsolutePath();
                  filePath = 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("*/*");
          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, FCR);
          return true;
      }
    });
    
  2. 選完照片之后

    /**
    * 打開圖庫,同時處理圖片
    */
    private void selectImage() {
       compressPath = Environment.getExternalStorageDirectory().getPath() + "/QWB/temp";
       File file = new File(compressPath);
       if (!file.exists()) {
           file.mkdirs();
       }
       compressPath = compressPath + File.separator + "compress.png";
       File image = new File(compressPath);
       if (image.exists()) {
           image.delete();
       }
    }
    
    // Create an image file
    private File createImageFile() throws IOException {
       @SuppressLint("SimpleDateFormat") String timeStamp = DateUtils.nowTimeDetail();
       String imageFileName = "img_" + timeStamp + "_";
       File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
       return File.createTempFile(imageFileName, ".jpg", storageDir);
    }
    
    private String mCM;
    private String filePath = "";
    private ValueCallback<Uri> mUM;
    private ValueCallback<Uri[]> mUMA;
    private final static int FCR = 1;
    String compressPath = "";
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
       super.onActivityResult(requestCode, resultCode, intent);
       if (Build.VERSION.SDK_INT >= 21) {
           Uri[] results = null;
           //Check if response is positive
           if (resultCode == Activity.RESULT_OK) {
               if (requestCode == FCR) {
                   if (null == mUMA) {
                       return;
                   }
                   if (intent == null) {
                       //Capture Photo if no image available
                       if (mCM != null) {
                           // results = new Uri[]{Uri.parse(mCM)};
                           results = new Uri[]{afterChosePic(filePath, compressPath)};
                       }
                   } else {
                       String dataString = intent.getDataString();
                       if (dataString != null) {
                           results = new Uri[]{Uri.parse(dataString)};
                           LogUtil.d("tag", intent.toString());
    //                            String realFilePath = getRealFilePath(Uri.parse(dataString));
    //                            results = new Uri[]{afterChosePic(realFilePath, compressPath)};
                       }
                   }
               }
           }
           mUMA.onReceiveValue(results);
           mUMA = null;
       } else {
           if (requestCode == FCR) {
               if (null == mUM) return;
               Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
               mUM.onReceiveValue(result);
               mUM = null;
           }
       }
    }
    
    /**
    * 選擇照片后結(jié)束
    */
    private Uri afterChosePic(String oldPath, String newPath) {
       File newFile;
       try {
           newFile = FileUtils.compressFile(oldPath, newPath);
       } catch (Exception e) {
           e.printStackTrace();
           newFile = null;
       }
       return Uri.fromFile(newFile);
    }
    
  3. 工具類

    public class FileUtils {
        /**
         * 把圖片壓縮到200K
         *
         * @param oldpath
         *            壓縮前的圖片路徑
         * @param newPath
         *            壓縮后的圖片路徑
         * @return
         */
        public static File compressFile(String oldpath, String newPath) {
            Bitmap compressBitmap = FileUtils.decodeFile(oldpath);
            Bitmap newBitmap = ratingImage(oldpath, compressBitmap);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            newBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
            byte[] bytes = os.toByteArray();
    
            File file = null ;
            try {
                file = FileUtils.getFileFromBytes(bytes, newPath);
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                if(newBitmap != null ){
                    if(!newBitmap.isRecycled()){
                        newBitmap.recycle();
                    }
                    newBitmap  = null;
                }
                if(compressBitmap != null ){
                    if(!compressBitmap.isRecycled()){
                        compressBitmap.recycle();
                    }
                    compressBitmap  = null;
                }
            }
            return file;
        }
    
        private static Bitmap ratingImage(String filePath,Bitmap bitmap){
            int degree = readPictureDegree(filePath);
            return rotaingImageView(degree, bitmap);
        }
    
        /**
         *  旋轉(zhuǎn)圖片
         * @param angle
         * @param bitmap
         * @return Bitmap
         */
        public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
            //旋轉(zhuǎn)圖片 動作
            Matrix matrix = new Matrix();;
            matrix.postRotate(angle);
            System.out.println("angle2=" + angle);
            // 創(chuàng)建新的圖片
            Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                    bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            return resizedBitmap;
        }
    
        /**
         * 讀取圖片屬性:旋轉(zhuǎn)的角度
         * @param path 圖片絕對路徑
         * @return degree旋轉(zhuǎn)的角度
         */
        public static int readPictureDegree(String path) {
            int degree  = 0;
            try {
                ExifInterface exifInterface = new ExifInterface(path);
                int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
                switch (orientation) {
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        degree = 90;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        degree = 180;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        degree = 270;
                        break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return degree;
        }
    
        /**
         * 把字節(jié)數(shù)組保存為一個文件
         *
         * @param b
         * @param outputFile
         * @return
         */
        public static File getFileFromBytes(byte[] b, String outputFile) {
            File ret = null;
            BufferedOutputStream stream = null;
            try {
                ret = new File(outputFile);
                FileOutputStream fstream = new FileOutputStream(ret);
                stream = new BufferedOutputStream(fstream);
                stream.write(b);
            } catch (Exception e) {
                // log.error("helper:get file from byte process error!");
                e.printStackTrace();
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        // log.error("helper:get file from byte process error!");
                        e.printStackTrace();
                    }
                }
            }
            return ret;
        }
    
        /**
         * 圖片壓縮
         *
         * @param fPath
         * @return
         */
        public static Bitmap decodeFile(String fPath) {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            opts.inDither = false; // Disable Dithering mode
            opts.inPurgeable = true; // Tell to gc that whether it needs free
            opts.inInputShareable = true; // Which kind of reference will be used to
            BitmapFactory.decodeFile(fPath, opts);
            final int REQUIRED_SIZE = 400;
            int scale = 1;
            if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {
                final int heightRatio = Math.round((float) opts.outHeight
                        / (float) REQUIRED_SIZE);
                final int widthRatio = Math.round((float) opts.outWidth
                        / (float) REQUIRED_SIZE);
                scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
            }
            Log.i("scale", "scal ="+ scale);
            opts.inJustDecodeBounds = false;
            opts.inSampleSize = scale;
            Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(Bitmap.Config.ARGB_8888, false);
            return bm;
        }
    
    
    
        /**
         * 創(chuàng)建目錄
         * @param path
         */
        public static void setMkdir(String path)
        {
            File file = new File(path);
            if(!file.exists())
            {
                file.mkdirs();
                Log.e("file", "目錄不存在  創(chuàng)建目錄    ");
            }else{
                Log.e("file", "目錄存在");
            }
        }
    
        /**
         * 獲取目錄名稱
         * @param url
         * @return FileName
         */
        public static String getFileName(String url)
        {
            int lastIndexStart = url.lastIndexOf("/");
            if(lastIndexStart!=-1)
            {
                return url.substring(lastIndexStart+1, url.length());
            }else{
                return null;
            }
        }
    
        /**
         * 刪除該目錄下的文件
         *
         * @param path
         */
        public static void delFile(String path) {
            if (!TextUtils.isEmpty(path)) {
                File file = new File(path);
                if (file.exists()) {
                    file.delete();
                }
            }
        }
    }
    
  4. 需要注意的問題

    1. 在打release包的時候鲫剿,因為混淆的問題鳄逾,點擊又會沒有反應(yīng),這是因為openFileChooser()是系統(tǒng)api牵素,所以需要在混淆是不混淆該方法严衬。

      -keepclassmembers class * extends android.webkit.WebChromeClient{
      public void openFileChooser(...);
      }
      
    2. 當點擊拍照之后,如果相機是橫屏拍照的話笆呆,當拍照結(jié)束之后跳回app的時候请琳,會導致app端當前的webView頁面銷毀并重新打開,需要在androidManifest.xml中當前Activity添加:

      android:configChanges="orientation|keyboardHidden|screenSize"
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赠幕,一起剝皮案震驚了整個濱河市俄精,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榕堰,老刑警劉巖竖慧,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逆屡,居然都是意外死亡圾旨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門魏蔗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砍的,“玉大人,你說我怎么就攤上這事莺治±希” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵谣旁,是天一觀的道長床佳。 經(jīng)常有香客問我,道長榄审,這世上最難降的妖魔是什么砌们? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上怨绣,老公的妹妹穿的比我還像新娘角溃。我一直安慰自己拷获,他們只是感情好篮撑,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匆瓜,像睡著了一般赢笨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驮吱,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天茧妒,我揣著相機與錄音,去河邊找鬼左冬。 笑死桐筏,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拇砰。 我是一名探鬼主播梅忌,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼除破!你這毒婦竟也來了牧氮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤瑰枫,失蹤者是張志新(化名)和其女友劉穎踱葛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體光坝,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡尸诽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盯另。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片性含。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖土铺,靈堂內(nèi)的尸體忽然破棺而出胶滋,到底是詐尸還是另有隱情,我是刑警寧澤悲敷,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布究恤,位于F島的核電站,受9級特大地震影響后德,放射性物質(zhì)發(fā)生泄漏部宿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望理张。 院中可真熱鬧赫蛇,春花似錦、人聲如沸雾叭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽织狐。三九已至暂幼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間移迫,已是汗流浹背旺嬉。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留厨埋,地道東北人邪媳。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像荡陷,于是被迫代替她去往敵國和親雨效。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容