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