1.FileProvider用于解决高版本的android系统出现的调用相机和相册不成功的问题
①. 权限申请
建议在你的BaseActivity中去增加两个方法。
public void onPermissionRequests(String permission, OnBooleanListener onBooleanListener) { onPermissionListener = onBooleanListener; Log.d("MainActivity", "0"); if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? Log.d("MainActivity", "1"); if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) { //权限已有 onPermissionListener.onClick(true); } else { //没有权限,申请一下 ActivityCompat.requestPermissions(this, new String[]{permission}, 1); } }else{ onPermissionListener.onClick(true); Log.d("MainActivity", "2"+ContextCompat.checkSelfPermission(this, permission)); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 1) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //权限通过 if (onPermissionListener != null) { onPermissionListener.onClick(true); } } else { //权限拒绝 if (onPermissionListener != null) { onPermissionListener.onClick(false); } } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
然后在你需要申请权限的地方调用这个方法就可以了
onPermissionRequests
②. 7.0相机问题
Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException,因为在Android7.0之后Google认为直接使用本地
的根目录即file:// URI是不安全的操作,直接访问会抛出FileUriExposedExCeption异常,这就意味着在Android7.0以前
我们访问相机拍照存储时,如果使用URI的方式直接存储剪裁图片就会造成这个异常,那么如何解决这个问题呢?
Google为我们提供了FileProvider类,进行一种特殊的内容提供,FileProvider时ContentProvide的子类,它使用了和
内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。下面
就让我们看一下如何使用这个内容提供者进行数据访问的:
使用FileProvider获取Uri就会将以前的file:// URI准换成content:// URI,实现一种安全的应用间数据访问,内容提
供者作为Android的四大组件之一,使用同样需要在清单文件AndroidManifest.xml中进行注册的,注册方法如下:首先
是准备工作,先在res文件夹下添加xml文件夹,然后创建一个文件file_paths,内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!--填写你要所要申请访问的目录地址,name最好是你的目录名,path是你要申请的目录--> <external-path name="camera_photos" path="." /> </paths>
然后在AndroidManifest.xml文件中添加
<!-- 这里直接复制就可以,需要注意的是authorities,以你的包名加上fileprovider,因为他需要唯一 --> <provider android:name="android.support.v4.content.FileProvider" android:authorities="包名.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>
这样准备工作就做好了,不要纠结着FileProvider,到时候直接用就可以,下面是调用代码:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File photoFile = createImagePathFile(MainActivity.this); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); /* * 这里就是高版本需要注意的,需用使用FileProvider来获取Uri,同时需要注意getUriForFile * 方法第二个参数要与AndroidManifest.xml中provider的里面的属性authorities的值一致 * */ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); imageUriFromCamera = FileProvider.getUriForFile(MainActivity.this, "包名.fileprovider", photoFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUriFromCamera); startActivityForResult(intent, GET_IMAGE_BY_CAMERA_U);
然后就没有然后了,结尾附几个Demo,很简单:
https://github.com/xuezj/FileProviderDemo
https://github.com/zhengzhong1/Android6.0PermissionsDemo
https://github.com/HelloWmz/PhotoDemo
2.直接在webview中点击事件调用摄像头和相册就得重写webcromeClient方法
WebViewClient webViewClient = new WebViewClient() { public OkHttpClient client = new OkHttpClient(); @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { // ------5.0以上手机执行------ Uri uri = request.getUrl(); String url = uri.toString(); return shouldInterceptRequest(view, url); } @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { String newUrl = StringUtil.getNewUrl(url); Utils.CommonUtilLog(TAG, "old url=" + url); Utils.CommonUtilLog(TAG, "new url=" + newUrl); if (newUrl.equals(url)) { return super.shouldInterceptRequest(view, url); } Request request1 = new Request.Builder() .url(newUrl) .build(); Response response = null; try { response = client.newCall(request1).execute(); } catch (IOException e) { e.printStackTrace(); } String type = Utils.getMimeType(newUrl); Utils.CommonUtilLog(TAG, "type=" + type); return new WebResourceResponse(type, response.header("content-encoding", "utf-8"), response.body().byteStream()); } }; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) { mWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); } @Override protected void onPause() { super.onPause(); } @Override protected void onDestroy() { super.onDestroy(); if (mWebView != null) { mWebView.clearHistory(); ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.loadUrl("about:blank"); mWebView.stopLoading(); mWebView.setWebChromeClient(null); mWebView.setWebViewClient(null); mWebView.destroy(); mWebView = null; } } /* *=====HTML5调相机相册====== * 重写WebChromeClient方法 */ WebChromeClient webChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { mUploadCallbackAboveL = filePathCallback; take(); return true; } public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; take(); } public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMessage = uploadMsg; take(); } public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { mUploadMessage = uploadMsg; take(); } }; private void take() { 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(); final Intent captureIntent = new Intent(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); } 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[]{})); RemoteWebActivity.this.startActivityForResult(chooserIntent, FILE_CHOOSER_RESULT_CODE); } @SuppressWarnings("null") @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) { if (requestCode != FILE_CHOOSER_RESULT_CODE || mUploadCallbackAboveL == null) { return; } Uri[] results = null; if (resultCode == Activity.RESULT_OK) { if (data == null) { results = new Uri[]{imageUri}; } else { String dataString = data.getDataString(); ClipData clipData = data.getClipData(); if (clipData != null) { results = new Uri[clipData.getItemCount()]; for (int i = 0; i < clipData.getItemCount(); i++) { ClipData.Item item = clipData.getItemAt(i); results[i] = item.getUri(); } } if (dataString != null) results = new Uri[]{Uri.parse(dataString)}; } } if (results != null) { mUploadCallbackAboveL.onReceiveValue(results); mUploadCallbackAboveL = null; } else { results = new Uri[]{imageUri}; mUploadCallbackAboveL.onReceiveValue(results); mUploadCallbackAboveL = null; } return; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == FILE_CHOOSER_RESULT_CODE) { if (null == mUploadMessage && null == mUploadCallbackAboveL) return; Uri result = data == null || resultCode != RESULT_OK ? null : data.getData(); if (mUploadCallbackAboveL != null) { onActivityResultAboveL(requestCode, resultCode, data); } else if (mUploadMessage != null) { if (result != null) { String path = getPath(getApplicationContext(), result); Uri uri = Uri.fromFile(new File(path)); mUploadMessage .onReceiveValue(uri); } else { mUploadMessage.onReceiveValue(imageUri); } mUploadMessage = null; } } } @SuppressLint("NewApi") @TargetApi(Build.VERSION_CODES.KITKAT) public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); }
在h5界面必须有那么一行代码
<div class="jdmodel-first-bottom">
<img class="jdmodel-first-bottom-button" src="/statics/jdmodel/css/image/bottom.png"/>
<input type="file" id="cameraInput" style="display: none" accept="image/*"/>
<img class="jdmodel-first-bottom-photograph" src="/statics/jdmodel/css/image/photograph.png" id="photoBtnId"/>
</div>
返回后的图片会放到id为cameraInput的界面位置!!!