问题

前几天接到的一个需求,是关于第三方理财产品的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"
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android
# input兼容
# WebView
# 上传照片
相关文章:
全景视频制作网站有哪些,全景图怎么做成网页?
如何在IIS中配置站点IP、端口及主机头?
制作宣传网站的软件,小红书可以宣传网站吗?
建站与域名管理如何高效结合?
网站制作费用多少钱,一个网站的运营,需要哪些费用?
番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?
湖州网站制作公司有哪些,浙江中蓝新能源公司官网?
建站主机是否等同于虚拟主机?
MySQL查询结果复制到新表的方法(更新、插入)
香港服务器WordPress建站指南:SEO优化与高效部署策略
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
Python文件管理规范_工程实践说明【指导】
创业网站制作流程,创业网站可靠吗?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
实现虚拟支付需哪些建站技术支撑?
如何选择适配移动端的WAP自助建站平台?
制作网站的软件下载免费,今日头条开宝箱老是需要下载怎么回事?
动图在线制作网站有哪些,滑动动图图集怎么做?
武清网站制作公司,天津武清个人营业执照注销查询系统网站?
南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?
武汉网站制作费用多少,在武汉武昌,建面100平方左右的房子,想装暖气片,费用大概是多少啊?
建站之星五站合一营销型网站搭建攻略,流量入口全覆盖优化指南
建站之星代理平台如何选择最佳方案?
七夕网站制作视频,七夕大促活动怎么报名?
香港服务器如何优化才能显著提升网站加载速度?
深圳防火门网站制作公司,深圳中天明防火门怎么编码?
如何在Golang中使用replace替换模块_指定本地或远程路径
专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?
为什么Go需要go mod文件_Go go mod文件作用说明
头像制作网站在线观看,除了站酷,还有哪些比较好的设计网站?
建站之星2.7模板:企业网站建设与h5定制设计专题
如何用免费手机建站系统零基础打造专业网站?
已有域名能否直接搭建网站?
如何挑选优质建站一级代理提升网站排名?
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
如何选择最佳自助建站系统?快速指南解析优劣
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
如何彻底卸载建站之星软件?
网站制作话术技巧,网站推广做的好怎么话术?
如何选择CMS系统实现快速建站与SEO优化?
教育培训网站制作流程,请问edu教育网站的域名怎么申请?
如何通过虚拟主机快速完成网站搭建?
如何用美橙互联一键搭建多站合一网站?
如何快速生成橙子建站落地页链接?
XML的“混合内容”是什么 怎么用DTD或XSD定义
如何在万网ECS上快速搭建专属网站?
网站制作新手教程,新手建设一个网站需要注意些什么?
教程网站设计制作软件,怎么创建自己的一个网站?
如何快速打造个性化非模板自助建站?
*请认真填写需求信息,我们会在24小时内与您取得联系。