相信大家在網上肯定有很多文章關于webview 調用相冊相機失敗問題 也有很多坑 是以我就不一一類舉了,小樓也是踩了很多坑 ,總結了很多才告知給各位,希望大家少踩坑,這裡包括對android 7.0 的适配 關于Android6.0動态權限管理适配我就沒有去寫出來了 這個很簡單. 可以參考鴻洋大神寫的android 6.0動态權限申請部落格,非常詳細。
webview 調用系統相冊相機 相信大家知道要重寫webcromeclient 方法,至于為什麼可以自行度娘,相信會有更完善的解釋,webcromeclient 主要是幫助webview 處理各種通知和請求事件的方法。
好了不多說
大家肯定知道在android7.0 之後檔案存儲的位置不再是以往那種很容易就能拿到,google 加強了這方面的安全機制,是以可以借助一個fileProvider類 ,我們可以用這個進行一種特殊的内容傳遞提供取到系統的某些檔案。
fileprovider 為android 四大元件之一,相信懂得人可以自行跳過 不懂的小白 還是老樣子 度娘教你.(本人初涉1年多了也才剛懂這個,抽象)
貼代碼
如果你要做7.0适配的話,要使用fileprovider 元件,這個必須在清單檔案進行注冊的,首先要在你的項目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="." />
<external-path name="cache" path="Android/data/com.example.app2/cache" />
<external-path name="images" path="Pictures/" />
<external-path name="dcim" path="DCIM/" />
</paths>
再在清單檔案中添加
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.app2.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
注意 android:authorites = ""; 這裡填你自己項目的包名.fileprovider
包名 清單檔案裡就有,以前我還看到有小白手打包名的
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app2">
package 後面就是你自己項目的包名
這樣fileprovider 就準備好了 ,可以在項目中快樂的使用了
下面貼主要代碼
private Uri imageUri;
//自定義 WebChromeClient 輔助WebView處理圖檔上傳操作【<input type=file> 檔案上傳标簽】
public class MyChromeWebClient extends WebChromeClient {
// For Android 3.0-
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg)");
mUploadMessage = uploadMsg;
take();
}
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
Log.d(TAG, "openFileChoose( ValueCallback uploadMsg, String acceptType )");
mUploadMessage = uploadMsg;
take();
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
mUploadMessage = uploadMsg;
take();
}
// For Android 5.0+
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
Log.d(TAG, "onShowFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
mUploadCallbackAboveL = filePathCallback;
take();
return true;
}
}
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");
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(AndroidBuildActivity.this, "com.example.app2.fileprovider", file);
} else {
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[]{}));
AndroidBuildActivity.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)};
}
} else {
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
return;
}
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());
}
這套代碼就可以完美解決webview 調用相機相冊失敗問題
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");
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(AndroidBuildActivity.this, "com.example.app2.fileprovider", file);
} else {
imageUri = Uri.fromFile(file);
}
這裡就是适配android7.0 的判斷 ,android7.0 正好對應API=24
注意當不是7.0的話 還是老樣子 從之前的位置擷取檔案
imageUri = FileProvider.getUriForFile(AndroidBuildActivity.this, "com.example.app2.fileprovider", file);
這句代碼就是使用fileprovier 擷取真實路徑位置,注意這邊的 “com.example.app2.fileprovider”要和你清單檔案裡面的一模一樣 ,要不然還是拿不到的。
在h5頁面要有這一行代碼
<input type="file" id="cameraInput" style="display: none" accept="image/*"/>
如果成功後圖檔将傳回到id= cameraInput這個位置