1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.browser;
18 
19 import android.app.Activity;
20 import android.content.ActivityNotFoundException;
21 import android.content.ClipData;
22 import android.content.Intent;
23 import android.net.Uri;
24 import android.provider.MediaStore;
25 import android.support.v4.content.FileProvider;
26 import android.webkit.WebChromeClient.FileChooserParams;
27 import android.webkit.ValueCallback;
28 import android.widget.Toast;
29 
30 import java.io.File;
31 
32 /**
33  * Handle the file upload. This does not support selecting multiple files yet.
34  */
35 public class UploadHandler {
36     private final static String IMAGE_MIME_TYPE = "image/*";
37     private final static String VIDEO_MIME_TYPE = "video/*";
38     private final static String AUDIO_MIME_TYPE = "audio/*";
39 
40     private final static String FILE_PROVIDER_AUTHORITY = "com.android.browser-classic.file";
41 
42     /*
43      * The Object used to inform the WebView of the file to upload.
44      */
45     private ValueCallback<Uri[]> mUploadMessage;
46 
47     private boolean mHandled;
48     private Controller mController;
49     private FileChooserParams mParams;
50     private Uri mCapturedMedia;
51 
UploadHandler(Controller controller)52     public UploadHandler(Controller controller) {
53         mController = controller;
54     }
55 
handled()56     boolean handled() {
57         return mHandled;
58     }
59 
onResult(int resultCode, Intent intent)60     void onResult(int resultCode, Intent intent) {
61         Uri[] uris;
62         // As the media capture is always supported, we can't use
63         // FileChooserParams.parseResult().
64         uris = parseResult(resultCode, intent);
65         mUploadMessage.onReceiveValue(uris);
66         mHandled = true;
67     }
68 
openFileChooser(ValueCallback<Uri[]> callback, FileChooserParams fileChooserParams)69     void openFileChooser(ValueCallback<Uri[]> callback, FileChooserParams fileChooserParams) {
70 
71         if (mUploadMessage != null) {
72             // Already a file picker operation in progress.
73             return;
74         }
75 
76         mUploadMessage = callback;
77         mParams = fileChooserParams;
78         Intent[] captureIntents = createCaptureIntent();
79         assert(captureIntents != null && captureIntents.length > 0);
80         Intent intent = null;
81         // Go to the media capture directly if capture is specified, this is the
82         // preferred way.
83         if (fileChooserParams.isCaptureEnabled() && captureIntents.length == 1) {
84             intent = captureIntents[0];
85         } else {
86             intent = new Intent(Intent.ACTION_CHOOSER);
87             intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, captureIntents);
88             intent.putExtra(Intent.EXTRA_INTENT, fileChooserParams.createIntent());
89         }
90         startActivity(intent);
91     }
92 
parseResult(int resultCode, Intent intent)93     private Uri[] parseResult(int resultCode, Intent intent) {
94         if (resultCode == Activity.RESULT_CANCELED) {
95             return null;
96         }
97         Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
98                 : intent.getData();
99 
100         // As we ask the camera to save the result of the user taking
101         // a picture, the camera application does not return anything other
102         // than RESULT_OK. So we need to check whether the file we expected
103         // was written to disk in the in the case that we
104         // did not get an intent returned but did get a RESULT_OK. If it was,
105         // we assume that this result has came back from the camera.
106         if (result == null && intent == null && resultCode == Activity.RESULT_OK
107                 && mCapturedMedia != null) {
108             result = mCapturedMedia;
109         }
110 
111         Uri[] uris = null;
112         if (result != null) {
113             uris = new Uri[1];
114             uris[0] = result;
115         }
116         return uris;
117     }
118 
startActivity(Intent intent)119     private void startActivity(Intent intent) {
120         try {
121             mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
122         } catch (ActivityNotFoundException e) {
123             // No installed app was able to handle the intent that
124             // we sent, so file upload is effectively disabled.
125             Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
126                     Toast.LENGTH_LONG).show();
127         }
128     }
129 
createCaptureIntent()130     private Intent[] createCaptureIntent() {
131         String mimeType = "*/*";
132         String[] acceptTypes = mParams.getAcceptTypes();
133         if ( acceptTypes != null && acceptTypes.length > 0) {
134             mimeType = acceptTypes[0];
135         }
136         Intent[] intents;
137         if (mimeType.equals(IMAGE_MIME_TYPE)) {
138             intents = new Intent[1];
139             intents[0] = createCameraIntent(createTempFileContentUri(".jpg"));
140         } else if (mimeType.equals(VIDEO_MIME_TYPE)) {
141             intents = new Intent[1];
142             intents[0] = createCamcorderIntent();
143         } else if (mimeType.equals(AUDIO_MIME_TYPE)) {
144             intents = new Intent[1];
145             intents[0] = createSoundRecorderIntent();
146         } else {
147             intents = new Intent[3];
148             intents[0] = createCameraIntent(createTempFileContentUri(".jpg"));
149             intents[1] = createCamcorderIntent();
150             intents[2] = createSoundRecorderIntent();
151         }
152         return intents;
153     }
154 
createTempFileContentUri(String suffix)155     private Uri createTempFileContentUri(String suffix) {
156         try {
157             File mediaPath = new File(mController.getActivity().getFilesDir(), "captured_media");
158             if (!mediaPath.exists() && !mediaPath.mkdir()) {
159                 throw new RuntimeException("Folder cannot be created.");
160             }
161             File mediaFile = File.createTempFile(
162                     String.valueOf(System.currentTimeMillis()), suffix, mediaPath);
163             return FileProvider.getUriForFile(mController.getActivity(),
164                     FILE_PROVIDER_AUTHORITY, mediaFile);
165         } catch (java.io.IOException e) {
166             throw new RuntimeException(e);
167         }
168     }
169 
createCameraIntent(Uri contentUri)170     private Intent createCameraIntent(Uri contentUri) {
171         if (contentUri == null) throw new IllegalArgumentException();
172         mCapturedMedia = contentUri;
173         Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
174         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
175                   Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
176         intent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedMedia);
177         intent.setClipData(ClipData.newUri(mController.getActivity().getContentResolver(),
178                 FILE_PROVIDER_AUTHORITY, mCapturedMedia));
179         return intent;
180     }
181 
createCamcorderIntent()182     private Intent createCamcorderIntent() {
183         return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
184     }
185 
createSoundRecorderIntent()186     private Intent createSoundRecorderIntent() {
187         return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
188     }
189 }
190