欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android WebView的input上传照片的兼容问题

程序员文章站 2024-02-09 17:33:28
问题 前几天接到的一个需求,是关于第三方理财产品的h5上传照片问题。 对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的app端,ios...

问题

前几天接到的一个需求,是关于第三方理财产品的h5上传照片问题。

对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的app端,ios端上传没有问题,而android端则点击没有任何反应。

对方h5调用的方式是通过<input type='file' accept='image/*'/>的方式调用,本来以为这个问题很简单,就是app端没有设置相机权限,造成的点击无反应情况,而实际上加了之后发现,并非简单的权限问题。

解决问题

因为android的版本碎片问题,很多版本的webview都对唤起函数有不同的支持。

我们需要重写webchromeclient下的openfilechooser()(5.0及以上系统回调onshowfilechooser())。我们通过intent在openfilechooser()中唤起系统相机和支持intent的相关app。

在系统相机或者相关app中一顿操作之后,当返回app的时候,我们在onactivityresult()中将选择好的图片通过valuecallback的onreceivevalue方法返回给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;
    }
  }
}

/**
* 选择照片后结束
*/
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);
  }

  /**
   * 旋转图片
   * @param angle
   * @param bitmap
   * @return bitmap
   */
  public static bitmap rotaingimageview(int angle , bitmap bitmap) {
    //旋转图片 动作
    matrix matrix = new matrix();;
    matrix.postrotate(angle);
    system.out.println("angle2=" + angle);
    // 创建新的图片
    bitmap resizedbitmap = bitmap.createbitmap(bitmap, 0, 0,
        bitmap.getwidth(), bitmap.getheight(), matrix, true);
    return resizedbitmap;
  }

  /**
   * 读取图片属性:旋转的角度
   * @param path 图片绝对路径
   * @return degree旋转的角度
   */
  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;
  }

  /**
   * 把字节数组保存为一个文件
   *
   * @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;
  }

 

  /**
   * 创建目录
   * @param path
   */
  public static void setmkdir(string path)
  {
    file file = new file(path);
    if(!file.exists())
    {
      file.mkdirs();
      log.e("file", "目录不存在 创建目录  ");
    }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、需要注意的问题

在打release包的时候,因为混淆的问题,点击又会没有反应,这是因为openfilechooser()是系统api,所以需要在混淆是不混淆该方法。

-keepclassmembers class * extends android.webkit.webchromeclient{
public void openfilechooser(...);
}

当点击拍照之后,如果相机是横屏拍照的话,当拍照结束之后跳回app的时候,会导致app端当前的webview页面销毁并重新打开,需要在androidmanifest.xml中当前activity添加:

android:configchanges="orientation|keyboardhidden|screensize"

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。