diff options
author | qinmin <qinmin@chromium.org> | 2014-08-27 16:01:21 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-27 23:05:47 +0000 |
commit | 22b41158f1be60572f4eaa1da8676c36f69cb394 (patch) | |
tree | 192388f709b003a1d859429e68180b9643d8ebae | |
parent | 39a6a1b1e570f54aaa957095400b6d40aae0e0f1 (diff) | |
download | chromium_src-22b41158f1be60572f4eaa1da8676c36f69cb394.zip chromium_src-22b41158f1be60572f4eaa1da8676c36f69cb394.tar.gz chromium_src-22b41158f1be60572f4eaa1da8676c36f69cb394.tar.bz2 |
Use content URI to upload photos taken by camera
Currently chrome creates a file path when uploading a photo taken through camera
However, this doesn't always work.
Instead, we should use content URI. See more details in the crbug.
BUG=405593
Review URL: https://codereview.chromium.org/489053003
Cr-Commit-Position: refs/heads/master@{#292257}
8 files changed, 108 insertions, 24 deletions
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java index b806b47..70c27ed 100644 --- a/base/android/java/src/org/chromium/base/ContentUriUtils.java +++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java @@ -11,15 +11,43 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.File; + /** * This class provides methods to access content URI schemes. */ public abstract class ContentUriUtils { private static final String TAG = "ContentUriUtils"; + private static FileProviderUtil sFileProviderUtil; + + /** + * Provides functionality to translate a file into a content URI for use + * with a content provider. + */ + public interface FileProviderUtil { + /** + * Generate a content uri from the given file. + * @param context Application context. + * @param file The file to be translated. + */ + public Uri getContentUriFromFile(Context context, File file); + } // Prevent instantiation. private ContentUriUtils() {} + public static void setFileProviderUtil(FileProviderUtil util) { + sFileProviderUtil = util; + } + + public static Uri getContentUriFromFile(Context context, File file) { + ThreadUtils.assertOnUiThread(); + if (sFileProviderUtil != null) { + return sFileProviderUtil.getContentUriFromFile(context, file); + } + return null; + } + /** * Opens the content URI for reading, and returns the file descriptor to * the caller. The caller is responsible for closing the file desciptor. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/FileProviderHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/FileProviderHelper.java new file mode 100644 index 0000000..7576389 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/FileProviderHelper.java @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import android.content.Context; +import android.net.Uri; +import android.support.v4.content.FileProvider; + +import org.chromium.base.ContentUriUtils; + +import java.io.File; + +/** + * Utilities for translating a file into content URI. + */ +public class FileProviderHelper implements ContentUriUtils.FileProviderUtil { + // Keep this variable in sync with the value defined in file_paths.xml. + private static final String API_AUTHORITY_SUFFIX = ".FileProvider"; + + @Override + public Uri getContentUriFromFile(Context context, File file) { + return FileProvider.getUriForFile(context, + context.getPackageName() + API_AUTHORITY_SUFFIX, file); + } +} diff --git a/chrome/android/shell/java/AndroidManifest.xml b/chrome/android/shell/java/AndroidManifest.xml index ad27000..84ca1957 100644 --- a/chrome/android/shell/java/AndroidManifest.xml +++ b/chrome/android/shell/java/AndroidManifest.xml @@ -217,6 +217,15 @@ android:authorities="org.chromium.chrome.shell" android:exported="true" /> + <!-- Provider for FileProvider. --> + <provider android:name="android.support.v4.content.FileProvider" + android:authorities="org.chromium.chrome.shell.FileProvider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/file_paths" /> + </provider> + <!-- Sync adapter for browser sync. --> <service android:exported="false" android:name="org.chromium.chrome.shell.sync.ChromeShellSyncAdapterService"> diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java index 05608d3..dd31f98 100644 --- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java +++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java @@ -20,9 +20,11 @@ import com.google.common.annotations.VisibleForTesting; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.BaseSwitches; import org.chromium.base.CommandLine; +import org.chromium.base.ContentUriUtils; import org.chromium.base.MemoryPressureListener; import org.chromium.base.library_loader.ProcessInitException; import org.chromium.chrome.browser.DevToolsServer; +import org.chromium.chrome.browser.FileProviderHelper; import org.chromium.chrome.browser.appmenu.AppMenuHandler; import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils; @@ -173,6 +175,7 @@ public class ChromeShellActivity extends Activity implements AppMenuPropertiesDe // In case this method is called after the first onStart(), we need to inform the // SyncController that we have started. mSyncController.onStart(); + ContentUriUtils.setFileProviderUtil(new FileProviderHelper()); } @Override diff --git a/chrome/android/shell/res/xml/file_paths.xml b/chrome/android/shell/res/xml/file_paths.xml new file mode 100644 index 0000000..f40426a --- /dev/null +++ b/chrome/android/shell/res/xml/file_paths.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- The attributes in this XML file provide configuration information --> +<!-- for the ContentProvider. --> + +<paths xmlns:android="http://schemas.android.com/apk/res/android"> + <files-path name="images" path="images/"/> +</paths> diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 85a1330..e97ccc3 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -653,6 +653,7 @@ '../printing/printing.gyp:printing_java', '../sync/sync.gyp:sync_java', '../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib', + '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib', '../third_party/guava/guava.gyp:guava_javalib', '../ui/android/ui_android.gyp:ui_java', ], diff --git a/remoting/remoting_android.gypi b/remoting/remoting_android.gypi index b9f0778..744b302 100644 --- a/remoting/remoting_android.gypi +++ b/remoting/remoting_android.gypi @@ -102,12 +102,12 @@ ], }, 'dependencies': [ - 'android_support_v4_javalib_no_res', '../base/base.gyp:base_java', '../ui/android/ui_android.gyp:ui_java', 'remoting_android_resources', '../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib', '../third_party/android_tools/android_tools.gyp:android_support_v7_mediarouter_javalib', + '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib', ], 'includes': [ '../build/java.gypi' ], 'conditions' : [ @@ -155,16 +155,6 @@ }, 'includes': [ '../build/java_apk.gypi' ], }, # end of target 'remoting_test_apk' - { - # This jar contains the Android support v4 libary. It does not have - # any associated resources. - 'target_name': 'android_support_v4_javalib_no_res', - 'type': 'none', - 'variables': { - 'jar_path': '../third_party/android_tools/sdk/extras/android/support/v4/android-support-v4.jar', - }, - 'includes': ['../build/java_prebuilt.gypi'], - }, # end of target 'android_support_v4_javalib_no_res' ], # end of 'targets' 'conditions': [ ['enable_cast==1', { diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java index 05186eb..180b493 100644 --- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java +++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java @@ -8,13 +8,14 @@ import android.annotation.TargetApi; import android.app.Activity; import android.content.ClipData; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; -import android.os.Environment; import android.provider.MediaStore; import android.text.TextUtils; +import android.util.Log; import org.chromium.base.CalledByNative; import org.chromium.base.ContentUriUtils; @@ -22,6 +23,7 @@ import org.chromium.base.JNINamespace; import org.chromium.ui.R; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -32,6 +34,7 @@ import java.util.List; */ @JNINamespace("ui") class SelectFileDialog implements WindowAndroid.IntentCallback{ + private static final String TAG = "SelectFileDialog"; private static final String IMAGE_TYPE = "image/"; private static final String VIDEO_TYPE = "video/"; private static final String AUDIO_TYPE = "audio/"; @@ -40,6 +43,8 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*"; private static final String ANY_TYPES = "*/*"; private static final String CAPTURE_IMAGE_DIRECTORY = "browser-photos"; + // Keep this variable in sync with the value defined in file_paths.xml. + private static final String IMAGE_FILE_PATH = "images"; private final long mNativeSelectFileDialog; private List<String> mFileTypes; @@ -66,8 +71,23 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ Intent chooser = new Intent(Intent.ACTION_CHOOSER); Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - mCameraOutputUri = Uri.fromFile(getFileForImageCapture()); + camera.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | + Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + Context context = window.getApplicationContext(); + try { + mCameraOutputUri = ContentUriUtils.getContentUriFromFile( + context, getFileForImageCapture(context)); + } catch (IOException e) { + Log.e(TAG, "Cannot retrieve content uri from file", e); + } + if (mCameraOutputUri == null) { + onFileNotSelected(); + return; + } + camera.putExtra(MediaStore.EXTRA_OUTPUT, mCameraOutputUri); + camera.setClipData( + ClipData.newUri(context.getContentResolver(), IMAGE_FILE_PATH, mCameraOutputUri)); Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); Intent soundRecorder = new Intent( MediaStore.Audio.Media.RECORD_SOUND_ACTION); @@ -125,18 +145,16 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ } /** - * Get a file for the image capture in the CAPTURE_IMAGE_DIRECTORY directory. + * Get a file for the image capture in the IMAGE_FILE_PATH directory. + * @param context The application context. */ - private File getFileForImageCapture() { - File externalDataDir = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DCIM); - File cameraDataDir = new File(externalDataDir.getAbsolutePath() + - File.separator + CAPTURE_IMAGE_DIRECTORY); - if (!cameraDataDir.exists() && !cameraDataDir.mkdirs()) { - cameraDataDir = externalDataDir; + private File getFileForImageCapture(Context context) throws IOException { + final File path = new File(context.getFilesDir(), IMAGE_FILE_PATH); + if (!path.exists() && !path.mkdir()) { + throw new IOException("Folder cannot be created."); } - File photoFile = new File(cameraDataDir.getAbsolutePath() + - File.separator + System.currentTimeMillis() + ".jpg"); + File photoFile = File.createTempFile( + String.valueOf(System.currentTimeMillis()), ".jpg", path); return photoFile; } @@ -160,7 +178,8 @@ class SelectFileDialog implements WindowAndroid.IntentCallback{ if (results == null) { // If we have a successful return but no data, then assume this is the camera returning // the photo that we requested. - nativeOnFileSelected(mNativeSelectFileDialog, mCameraOutputUri.getPath(), ""); + nativeOnFileSelected(mNativeSelectFileDialog, mCameraOutputUri.toString(), + mCameraOutputUri.getLastPathSegment()); // Broadcast to the media scanner that there's a new photo on the device so it will // show up right away in the gallery (rather than waiting until the next time the media |