18233facddcc51865d612a919d450db6954aa48e3Michael Kolb/* 28233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Copyright (C) 2010 The Android Open Source Project 38233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 48233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 58233facddcc51865d612a919d450db6954aa48e3Michael Kolb * you may not use this file except in compliance with the License. 68233facddcc51865d612a919d450db6954aa48e3Michael Kolb * You may obtain a copy of the License at 78233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 88233facddcc51865d612a919d450db6954aa48e3Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 98233facddcc51865d612a919d450db6954aa48e3Michael Kolb * 108233facddcc51865d612a919d450db6954aa48e3Michael Kolb * Unless required by applicable law or agreed to in writing, software 118233facddcc51865d612a919d450db6954aa48e3Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 128233facddcc51865d612a919d450db6954aa48e3Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138233facddcc51865d612a919d450db6954aa48e3Michael Kolb * See the License for the specific language governing permissions and 148233facddcc51865d612a919d450db6954aa48e3Michael Kolb * limitations under the License. 158233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 168233facddcc51865d612a919d450db6954aa48e3Michael Kolb 178233facddcc51865d612a919d450db6954aa48e3Michael Kolbpackage com.android.browser; 188233facddcc51865d612a919d450db6954aa48e3Michael Kolb 198233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.app.Activity; 20e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdochimport android.content.ActivityNotFoundException; 21a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdochimport android.content.ClipData; 228233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.content.Intent; 238233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.net.Uri; 248233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.provider.MediaStore; 25126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Baiimport android.support.v4.content.FileProvider; 264d59434c396e3d414ed4266103d6990a340331e1Selim Gurunimport android.webkit.WebChromeClient.FileChooserParams; 278233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport android.webkit.ValueCallback; 28e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdochimport android.widget.Toast; 298233facddcc51865d612a919d450db6954aa48e3Michael Kolb 308233facddcc51865d612a919d450db6954aa48e3Michael Kolbimport java.io.File; 318233facddcc51865d612a919d450db6954aa48e3Michael Kolb 328233facddcc51865d612a919d450db6954aa48e3Michael Kolb/** 334d59434c396e3d414ed4266103d6990a340331e1Selim Gurun * Handle the file upload. This does not support selecting multiple files yet. 348233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 358233facddcc51865d612a919d450db6954aa48e3Michael Kolbpublic class UploadHandler { 36126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private final static String IMAGE_MIME_TYPE = "image/*"; 37126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private final static String VIDEO_MIME_TYPE = "video/*"; 38126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private final static String AUDIO_MIME_TYPE = "audio/*"; 398233facddcc51865d612a919d450db6954aa48e3Michael Kolb 40a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdoch private final static String FILE_PROVIDER_AUTHORITY = "com.android.browser-classic.file"; 41a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdoch 428233facddcc51865d612a919d450db6954aa48e3Michael Kolb /* 438233facddcc51865d612a919d450db6954aa48e3Michael Kolb * The Object used to inform the WebView of the file to upload. 448233facddcc51865d612a919d450db6954aa48e3Michael Kolb */ 454d59434c396e3d414ed4266103d6990a340331e1Selim Gurun private ValueCallback<Uri[]> mUploadMessage; 468233facddcc51865d612a919d450db6954aa48e3Michael Kolb 4751f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch private boolean mHandled; 488233facddcc51865d612a919d450db6954aa48e3Michael Kolb private Controller mController; 49126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private FileChooserParams mParams; 50126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private Uri mCapturedMedia; 518233facddcc51865d612a919d450db6954aa48e3Michael Kolb 528233facddcc51865d612a919d450db6954aa48e3Michael Kolb public UploadHandler(Controller controller) { 538233facddcc51865d612a919d450db6954aa48e3Michael Kolb mController = controller; 548233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 558233facddcc51865d612a919d450db6954aa48e3Michael Kolb 5651f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch boolean handled() { 5751f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch return mHandled; 5851f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch } 5951f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch 608233facddcc51865d612a919d450db6954aa48e3Michael Kolb void onResult(int resultCode, Intent intent) { 61126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Uri[] uris; 62126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // As the media capture is always supported, we can't use 63126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // FileChooserParams.parseResult(). 64126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai uris = parseResult(resultCode, intent); 65126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai mUploadMessage.onReceiveValue(uris); 6651f6a2f1dc524b31476fb71de36f2bd79f499d08Ben Murdoch mHandled = true; 678233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 688233facddcc51865d612a919d450db6954aa48e3Michael Kolb 694d59434c396e3d414ed4266103d6990a340331e1Selim Gurun void openFileChooser(ValueCallback<Uri[]> callback, FileChooserParams fileChooserParams) { 708233facddcc51865d612a919d450db6954aa48e3Michael Kolb 718233facddcc51865d612a919d450db6954aa48e3Michael Kolb if (mUploadMessage != null) { 728233facddcc51865d612a919d450db6954aa48e3Michael Kolb // Already a file picker operation in progress. 738233facddcc51865d612a919d450db6954aa48e3Michael Kolb return; 748233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 758233facddcc51865d612a919d450db6954aa48e3Michael Kolb 764d59434c396e3d414ed4266103d6990a340331e1Selim Gurun mUploadMessage = callback; 77126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai mParams = fileChooserParams; 78126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Intent[] captureIntents = createCaptureIntent(); 79126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai assert(captureIntents != null && captureIntents.length > 0); 80126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Intent intent = null; 81126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // Go to the media capture directly if capture is specified, this is the 82126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // preferred way. 83126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if (fileChooserParams.isCaptureEnabled() && captureIntents.length == 1) { 84126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent = captureIntents[0]; 85126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } else { 86126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent = new Intent(Intent.ACTION_CHOOSER); 87126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, captureIntents); 88126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent.putExtra(Intent.EXTRA_INTENT, fileChooserParams.createIntent()); 89126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 90126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai startActivity(intent); 91126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 92126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 93126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private Uri[] parseResult(int resultCode, Intent intent) { 94126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if (resultCode == Activity.RESULT_CANCELED) { 95126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai return null; 96126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 97126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Uri result = intent == null || resultCode != Activity.RESULT_OK ? null 98126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai : intent.getData(); 99126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 100126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // As we ask the camera to save the result of the user taking 101126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // a picture, the camera application does not return anything other 102126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // than RESULT_OK. So we need to check whether the file we expected 103126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // was written to disk in the in the case that we 104126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // did not get an intent returned but did get a RESULT_OK. If it was, 105126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai // we assume that this result has came back from the camera. 106126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if (result == null && intent == null && resultCode == Activity.RESULT_OK 107126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai && mCapturedMedia != null) { 108126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai result = mCapturedMedia; 109126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 110126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 111126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Uri[] uris = null; 112126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if (result != null) { 113126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai uris = new Uri[1]; 114126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai uris[0] = result; 115126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 116126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai return uris; 117e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch } 1188233facddcc51865d612a919d450db6954aa48e3Michael Kolb 119e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch private void startActivity(Intent intent) { 120e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch try { 121e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED); 122e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch } catch (ActivityNotFoundException e) { 123e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch // No installed app was able to handle the intent that 12479ff1e36c4660c532fd8e5f4ab264e967fe308d0Selim Gurun // we sent, so file upload is effectively disabled. 12579ff1e36c4660c532fd8e5f4ab264e967fe308d0Selim Gurun Toast.makeText(mController.getActivity(), R.string.uploads_disabled, 12679ff1e36c4660c532fd8e5f4ab264e967fe308d0Selim Gurun Toast.LENGTH_LONG).show(); 1278233facddcc51865d612a919d450db6954aa48e3Michael Kolb } 128e4c0cae39f1ca34891de0f892aee4797c892caecBen Murdoch } 129126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 130126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private Intent[] createCaptureIntent() { 131126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai String mimeType = "*/*"; 132126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai String[] acceptTypes = mParams.getAcceptTypes(); 133126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if ( acceptTypes != null && acceptTypes.length > 0) { 134126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai mimeType = acceptTypes[0]; 135126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 136126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Intent[] intents; 137126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai if (mimeType.equals(IMAGE_MIME_TYPE)) { 138126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents = new Intent[1]; 139481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai intents[0] = createCameraIntent(createTempFileContentUri(".jpg")); 140126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } else if (mimeType.equals(VIDEO_MIME_TYPE)) { 141126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents = new Intent[1]; 142126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents[0] = createCamcorderIntent(); 143126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } else if (mimeType.equals(AUDIO_MIME_TYPE)) { 144126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents = new Intent[1]; 145126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents[0] = createSoundRecorderIntent(); 146126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } else { 147126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents = new Intent[3]; 148481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai intents[0] = createCameraIntent(createTempFileContentUri(".jpg")); 149126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents[1] = createCamcorderIntent(); 150126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intents[2] = createSoundRecorderIntent(); 151126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 152126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai return intents; 153126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 154126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 155481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai private Uri createTempFileContentUri(String suffix) { 156481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai try { 157481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai File mediaPath = new File(mController.getActivity().getFilesDir(), "captured_media"); 158481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai if (!mediaPath.exists() && !mediaPath.mkdir()) { 159481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai throw new RuntimeException("Folder cannot be created."); 160481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai } 161481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai File mediaFile = File.createTempFile( 162481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai String.valueOf(System.currentTimeMillis()), suffix, mediaPath); 163481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai return FileProvider.getUriForFile(mController.getActivity(), 164a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdoch FILE_PROVIDER_AUTHORITY, mediaFile); 165481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai } catch (java.io.IOException e) { 166481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai throw new RuntimeException(e); 167481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai } 168481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai } 169481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai 170481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai private Intent createCameraIntent(Uri contentUri) { 171481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai if (contentUri == null) throw new IllegalArgumentException(); 172481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai mCapturedMedia = contentUri; 173126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 174126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | 175126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 176126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai intent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedMedia); 177a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdoch intent.setClipData(ClipData.newUri(mController.getActivity().getContentResolver(), 178a1f40f93fc1eb046a06683cafc62205a7ec6ca0cBen Murdoch FILE_PROVIDER_AUTHORITY, mCapturedMedia)); 179126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai return intent; 180126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 181126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 182126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private Intent createCamcorderIntent() { 183481681a1d37ea51a6b1ee53600efeb5013666c58Tao Bai return new Intent(MediaStore.ACTION_VIDEO_CAPTURE); 184126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 185126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai 186126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai private Intent createSoundRecorderIntent() { 187126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); 188126b5b3a771fbcf1a4acd1700c8cfa22975f22dfTao Bai } 1898233facddcc51865d612a919d450db6954aa48e3Michael Kolb} 190