0

Upstreaming SelectFileDialog for Android

Upstreaming the Select File Dialog and its dependencies needed for
Chrome on Android

BUG=116131


Review URL: https://chromiumcodereview.appspot.com/10916160

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157424 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
aurimas@chromium.org
2012-09-18 21:47:56 +00:00
parent 41a8da1d1c
commit 2d02a200c2
33 changed files with 950 additions and 43 deletions

@ -151,6 +151,7 @@ def JavaParamToJni(param):
'Lcom/google/android/apps/chrome/AutofillData',
'Lcom/google/android/apps/chrome/ChromeHttpAuthHandler',
'Lcom/google/android/apps/chrome/ChromeContextMenuInfo',
'Lcom/google/android/apps/chrome/ChromeWindow',
'Lcom/google/android/apps/chrome/OmniboxSuggestion',
'Lcom/google/android/apps/chrome/PageInfoViewer',
'Lcom/google/android/apps/chrome/Tab',
@ -170,7 +171,6 @@ def JavaParamToJni(param):
'Lorg/chromium/chrome/browser/FindNotificationDetails',
'Lorg/chromium/chrome/browser/JavascriptAppModalDialog',
'Lorg/chromium/chrome/browser/ProcessUtils',
'Lorg/chromium/chrome/browser/SelectFileDialog',
'Lorg/chromium/chrome/browser/component/web_contents_delegate_android/WebContentsDelegateAndroid',
'Lorg/chromium/content/browser/ContentVideoView',
'Lorg/chromium/content/browser/ContentViewClient',
@ -191,6 +191,8 @@ def JavaParamToJni(param):
'Lorg/chromium/media/MediaPlayerListener',
'Lorg/chromium/net/NetworkChangeNotifier',
'Lorg/chromium/net/ProxyChangeListener',
'Lorg/chromium/ui/gfx/NativeWindow',
'Lorg/chromium/ui/SelectFileDialog',
]
if param == 'byte[][]':
return '[[B'

@ -17,7 +17,7 @@ import android.widget.TextView;
import org.chromium.base.CalledByNative;
import org.chromium.content.app.AppResource;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.ui.gfx.NativeWindow;
public class JavascriptAppModalDialog {
private String mTitle;
@ -60,10 +60,9 @@ public class JavascriptAppModalDialog {
}
@CalledByNative
void showJavascriptAppModalDialog(ContentViewCore contentViewCore, int nativeDialogPointer) {
assert contentViewCore != null;
Context context = contentViewCore.getContext();
void showJavascriptAppModalDialog(NativeWindow window, int nativeDialogPointer) {
assert window != null;
Context context = window.getActivity();
// Cache the native dialog pointer so that we can use it to return the
// response.

@ -52,9 +52,6 @@ class TabAndroid {
const content::ContextMenuParams& params,
const base::Callback<void(int)>& callback) = 0;
virtual void ShowSelectFileDialog(
const base::android::ScopedJavaLocalRef<jobject>& select_file) = 0;
// --------------------------------------------------------------------------
// Public methods that call to Java via JNI
// --------------------------------------------------------------------------

@ -409,6 +409,12 @@ void FileSelectHelper::RunFileChooserOnUIThread(
gfx::NativeWindow owning_window =
platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView());
#if defined(OS_ANDROID)
// Android needs the original MIME types and an additional capture value.
std::vector<string16> accept_types(params.accept_types);
accept_types.push_back(params.capture);
#endif
select_file_dialog_->SelectFile(
dialog_type_,
params.title,
@ -419,7 +425,7 @@ void FileSelectHelper::RunFileChooserOnUIThread(
FILE_PATH_LITERAL(""),
owning_window,
#if defined(OS_ANDROID)
const_cast<content::FileChooserParams*>(&params));
&accept_types);
#else
NULL);
#endif

@ -4,6 +4,7 @@
#include "base/logging.h"
#include "chrome/browser/platform_util.h"
#include "content/public/browser/android/content_view_core.h"
namespace platform_util {
@ -23,7 +24,7 @@ void OpenExternal(const GURL& url) {
gfx::NativeWindow GetTopLevel(gfx::NativeView view) {
NOTIMPLEMENTED();
return view;
return view->GetWindowAndroid();
}
gfx::NativeView GetParent(gfx::NativeView view) {

@ -10,17 +10,16 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
#include "content/public/browser/android/content_view_core.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/javascript_message_type.h"
#include "jni/JavascriptAppModalDialog_jni.h"
#include "ui/gfx/android/window_android.h"
using base::android::AttachCurrentThread;
using base::android::ConvertUTF16ToJavaString;
using base::android::ScopedJavaLocalRef;
using content::BrowserThread;
using content::ContentViewCore;
// static
NativeAppModalDialog* NativeAppModalDialog::CreateNativeJavaScriptPrompt(

@ -1081,11 +1081,13 @@
'../base/base.gyp:base',
'../chrome/browser/component/components.gyp:web_contents_delegate_android_java',
'../content/content.gyp:content_java',
'../ui/ui.gyp:ui_java',
],
'export_dependent_settings': [
'../base/base.gyp:base',
'../chrome/browser/component/components.gyp:web_contents_delegate_android_java',
'../content/content.gyp:content_java',
'../ui/ui.gyp:ui_java',
],
'variables': {
'package_name': 'chrome',

@ -46,6 +46,7 @@ include_rules = [
"+third_party/WebKit/Source/Platform/chromium",
"+third_party/WebKit/Source/WebKit/chromium",
"+ui/android",
# Aura is analogous to Win32 or a Gtk, so it is allowed.
"+ui/aura",
"+ui/base",

@ -24,6 +24,7 @@
#include "content/public/common/content_switches.h"
#include "media/base/android/media_jni_registrar.h"
#include "net/android/net_jni_registrar.h"
#include "ui/android/ui_jni_registrar.h"
#include "jni/LibraryLoader_jni.h"
#include "ui/gfx/android/gfx_jni_registrar.h"
@ -66,6 +67,9 @@ static jboolean LibraryLoadedOnMainThread(JNIEnv* env, jclass clazz,
if (!net::android::RegisterJni(env))
return JNI_FALSE;
if (!ui::RegisterJni(env))
return JNI_FALSE;
if (!content::android::RegisterCommonJni(env))
return JNI_FALSE;

@ -35,6 +35,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/android/WebInputEventFactory.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/android/window_android.h"
#include "webkit/glue/webmenuitem.h"
#include "webkit/user_agent/user_agent_util.h"
@ -79,11 +80,13 @@ ContentViewCore* ContentViewCore::GetNativeContentViewCore(JNIEnv* env,
ContentViewCoreImpl::ContentViewCoreImpl(JNIEnv* env, jobject obj,
bool hardware_accelerated,
bool take_ownership_of_web_contents,
WebContents* web_contents)
WebContents* web_contents,
ui::WindowAndroid* window_android)
: java_ref_(env, obj),
web_contents_(static_cast<WebContentsImpl*>(web_contents)),
owns_web_contents_(take_ownership_of_web_contents),
tab_crashed_(false) {
tab_crashed_(false),
window_android_(window_android) {
DCHECK(web_contents) <<
"A ContentViewCoreImpl should be created with a valid WebContents.";
@ -539,6 +542,10 @@ void ContentViewCoreImpl::LoadUrl(
tab_crashed_ = false;
}
ui::WindowAndroid* ContentViewCoreImpl::GetWindowAndroid() {
return window_android_;
}
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
@ -547,10 +554,12 @@ void ContentViewCoreImpl::LoadUrl(
jint Init(JNIEnv* env, jobject obj,
jboolean hardware_accelerated,
jboolean take_ownership_of_web_contents,
jint native_web_contents) {
jint native_web_contents,
jint native_window) {
ContentViewCoreImpl* view = new ContentViewCoreImpl(
env, obj, hardware_accelerated, take_ownership_of_web_contents,
reinterpret_cast<WebContents*>(native_web_contents));
reinterpret_cast<WebContents*>(native_web_contents),
reinterpret_cast<ui::WindowAndroid*>(native_window));
return reinterpret_cast<jint>(view);
}

@ -24,6 +24,10 @@
struct WebMenuItem;
namespace ui {
class WindowAndroid;
}
namespace content {
class ContentViewClient;
class RenderWidgetHostViewAndroid;
@ -36,7 +40,8 @@ class ContentViewCoreImpl : public ContentViewCore,
jobject obj,
bool hardware_accelerated,
bool take_ownership_of_web_contents,
WebContents* web_contents);
WebContents* web_contents,
ui::WindowAndroid* window_android);
// ContentViewCore overrides
virtual void Destroy(JNIEnv* env, jobject obj) OVERRIDE;
@ -172,6 +177,7 @@ class ContentViewCoreImpl : public ContentViewCore,
WebContents* web_contents() const { return web_contents_; }
virtual void LoadUrl(NavigationController::LoadURLParams& params) OVERRIDE;
virtual ui::WindowAndroid* GetWindowAndroid() OVERRIDE;
private:
// NotificationObserver implementation.
@ -218,6 +224,9 @@ class ContentViewCoreImpl : public ContentViewCore,
// Whether the renderer backing this ContentViewCore has crashed.
bool tab_crashed_;
// The owning window that has a hold of main application activity.
ui::WindowAndroid* window_android_;
DISALLOW_COPY_AND_ASSIGN(ContentViewCoreImpl);
};

@ -79,7 +79,7 @@ gfx::NativeView WebContentsViewAndroid::GetContentNativeView() const {
}
gfx::NativeWindow WebContentsViewAndroid::GetTopLevelNativeWindow() const {
return content_view_core_;
return content_view_core_->GetWindowAndroid();
}
void WebContentsViewAndroid::GetContainerBounds(gfx::Rect* out) const {

@ -282,12 +282,14 @@
'dependencies': [
'../base/base.gyp:base',
'../net/net.gyp:net',
'../ui/ui.gyp:ui_java',
'common_aidl',
'content_common',
],
'export_dependent_settings': [
'../base/base.gyp:base',
'../net/net.gyp:net',
'../ui/ui.gyp:ui_java',
],
'variables': {
'package_name': 'content',

@ -603,6 +603,7 @@
'../base/base.gyp:base_java',
'../media/media.gyp:media_java',
'../net/net.gyp:net_java',
'../ui/ui.gyp:ui_java',
],
'actions': [
{

@ -20,6 +20,7 @@ import android.webkit.DownloadListener;
import android.widget.FrameLayout;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.ui.gfx.NativeWindow;
/**
* The containing view for {@link ContentViewCore} that exists in the Android UI hierarchy and
@ -96,12 +97,14 @@ public class ContentView extends FrameLayout implements ContentViewCore.Internal
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param nativeWebContents A pointer to the native web contents.
* @param nativeWindow An instance of the NativeWindow.
* @param personality One of {@link #PERSONALITY_CHROME} or {@link #PERSONALITY_VIEW}.
* @return A ContentView instance.
*/
public static ContentView newInstance(Context context, int nativeWebContents, int personality) {
return newInstance(context, nativeWebContents, null, android.R.attr.webViewStyle,
personality);
public static ContentView newInstance(Context context, int nativeWebContents,
NativeWindow nativeWindow, int personality) {
return newInstance(context, nativeWebContents, nativeWindow, null,
android.R.attr.webViewStyle, personality);
}
/**
@ -109,14 +112,16 @@ public class ContentView extends FrameLayout implements ContentViewCore.Internal
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param nativeWebContents A pointer to the native web contents.
* @param nativeWindow An instance of the NativeWindow.
* @param attrs The attributes of the XML tag that is inflating the view.
* @return A ContentView instance.
*/
public static ContentView newInstance(Context context, int nativeWebContents,
AttributeSet attrs) {
NativeWindow nativeWindow, AttributeSet attrs) {
// TODO(klobag): use the WebViewStyle as the default style for now. It enables scrollbar.
// When ContentView is moved to framework, we can define its own style in the res.
return newInstance(context, nativeWebContents, attrs, android.R.attr.webViewStyle);
return newInstance(context, nativeWebContents, nativeWindow, attrs,
android.R.attr.webViewStyle);
}
/**
@ -124,31 +129,34 @@ public class ContentView extends FrameLayout implements ContentViewCore.Internal
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param nativeWebContents A pointer to the native web contents.
* @param nativeWindow An instance of the NativeWindow.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyle The default style to apply to this view.
* @return A ContentView instance.
*/
public static ContentView newInstance(Context context, int nativeWebContents,
AttributeSet attrs, int defStyle) {
return newInstance(context, nativeWebContents, attrs, defStyle, PERSONALITY_VIEW);
NativeWindow nativeWindow, AttributeSet attrs, int defStyle) {
return newInstance(context, nativeWebContents, nativeWindow, attrs, defStyle,
PERSONALITY_VIEW);
}
private static ContentView newInstance(Context context, int nativeWebContents,
AttributeSet attrs, int defStyle, int personality) {
NativeWindow nativeWindow, AttributeSet attrs, int defStyle, int personality) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return new ContentView(context, nativeWebContents, attrs, defStyle, personality);
} else {
return new JellyBeanContentView(context, nativeWebContents, attrs, defStyle,
return new ContentView(context, nativeWebContents, nativeWindow, attrs, defStyle,
personality);
} else {
return new JellyBeanContentView(context, nativeWebContents, nativeWindow, attrs,
defStyle, personality);
}
}
protected ContentView(Context context, int nativeWebContents, AttributeSet attrs, int defStyle,
int personality) {
protected ContentView(Context context, int nativeWebContents, NativeWindow nativeWindow,
AttributeSet attrs, int defStyle, int personality) {
super(context, attrs, defStyle);
mContentViewCore = new ContentViewCore(context, personality);
mContentViewCore.initialize(this, this, true, nativeWebContents, false);
mContentViewCore.initialize(this, this, true, nativeWebContents, nativeWindow, false);
}
/**

@ -35,6 +35,7 @@ import org.chromium.content.browser.TouchPoint;
import org.chromium.content.browser.ZoomManager;
import org.chromium.content.common.CleanupReference;
import org.chromium.content.common.TraceEvent;
import org.chromium.ui.gfx.NativeWindow;
/**
* Provides a Java-side 'wrapper' around a WebContent (native) instance.
@ -311,6 +312,7 @@ public class ContentViewCore implements MotionEventDelegate {
* @param takeOwnershipOfWebContents Whether this object will take ownership of
* nativeWebContents over on its native side.
* @param nativeWebContents A pointer to the native web contents.
* @param nativeWindow An instance of the NativeWindow.
* @param isAccessFromFileURLsGrantedByDefault Default WebSettings configuration.
*/
// Perform important post-construction set up of the ContentViewCore.
@ -321,11 +323,11 @@ public class ContentViewCore implements MotionEventDelegate {
// We supply the nativeWebContents pointer here rather than in the constructor to allow us
// to set the private browsing mode at a later point for the WebView implementation.
public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
boolean takeOwnershipOfWebContents, int nativeWebContents,
boolean takeOwnershipOfWebContents, int nativeWebContents, NativeWindow nativeWindow,
boolean isAccessFromFileURLsGrantedByDefault) {
mContainerView = containerView;
mNativeContentViewCore = nativeInit(mHardwareAccelerated, takeOwnershipOfWebContents,
nativeWebContents);
nativeWebContents, nativeWindow.getNativePointer());
mCleanupReference = new CleanupReference(
this, new DestroyRunnable(mNativeContentViewCore));
mContentSettings = new ContentSettings(
@ -1327,7 +1329,7 @@ public class ContentViewCore implements MotionEventDelegate {
// The following methods are implemented at native side.
private native int nativeInit(boolean hardwareAccelerated, boolean takeOwnershipOfWebContents,
int webContentsPtr);
int webContentsPtr, int windowAndroidPtr);
private static native void nativeDestroy(int nativeContentViewCore);

@ -10,13 +10,15 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import org.chromium.ui.gfx.NativeWindow;
/**
* A version of {@link ContentView} that supports JellyBean features.
*/
class JellyBeanContentView extends ContentView {
JellyBeanContentView(Context context, int nativeWebContents, AttributeSet attrs, int defStyle,
int personality) {
super(context, nativeWebContents, attrs, defStyle, personality);
JellyBeanContentView(Context context, int nativeWebContents, NativeWindow nativeWindow,
AttributeSet attrs, int defStyle, int personality) {
super(context, nativeWebContents, nativeWindow, attrs, defStyle, personality);
}
@Override

@ -5,10 +5,15 @@
#ifndef CONTENT_PUBLIC_BROWSER_ANDROID_CONTENT_VIEW_CORE_H_
#define CONTENT_PUBLIC_BROWSER_ANDROID_CONTENT_VIEW_CORE_H_
#include "base/android/scoped_java_ref.h"
#include <jni.h>
class GURL;
namespace ui {
class WindowAndroid;
}
namespace content {
class WebContents;
@ -49,6 +54,7 @@ class ContentViewCore {
virtual base::android::ScopedJavaLocalRef<jobject> GetJavaObject() = 0;
virtual ui::WindowAndroid* GetWindowAndroid() = 0;
protected:
virtual ~ContentViewCore() {};
};

@ -15,6 +15,7 @@ import org.chromium.content.app.AppResource;
import org.chromium.content.app.LibraryLoader;
import org.chromium.content.browser.ContentView;
import org.chromium.content.common.CommandLine;
import org.chromium.ui.gfx.NativeWindow;
/**
* Activity for managing the Content Shell.
@ -42,6 +43,7 @@ public class ContentShellActivity extends Activity {
setContentView(R.layout.content_shell_activity);
mShellManager = (ShellManager) findViewById(R.id.shell_container);
mShellManager.setWindow(new NativeWindow(this));
String startupUrl = getUrlFromIntent(getIntent());
if (!TextUtils.isEmpty(startupUrl)) {

@ -23,6 +23,7 @@ import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.LoadUrlParams;
import org.chromium.ui.gfx.NativeWindow;
/**
* Container for the various UI components that make up a shell window.
@ -48,6 +49,7 @@ public class Shell extends LinearLayout {
private ClipDrawable mProgressDrawable;
private View mSurfaceView;
private NativeWindow mWindow;
/**
* Constructor for inflating via XML.
@ -67,6 +69,13 @@ public class Shell extends LinearLayout {
FrameLayout.LayoutParams.MATCH_PARENT));
}
/**
* @param window The owning window for this shell.
*/
public void setWindow(NativeWindow window) {
mWindow = window;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@ -173,7 +182,7 @@ public class Shell extends LinearLayout {
@CalledByNative
private void initFromNativeTabContents(int nativeTabContents) {
mContentView = ContentView.newInstance(
getContext(), nativeTabContents, ContentView.PERSONALITY_CHROME);
getContext(), nativeTabContents, mWindow, ContentView.PERSONALITY_CHROME);
if (mContentView.getUrl() != null) mUrlTextView.setText(mContentView.getUrl());
((FrameLayout) findViewById(R.id.contentview_holder)).addView(mContentView,
new FrameLayout.LayoutParams(

@ -14,6 +14,7 @@ import android.widget.FrameLayout;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.ui.gfx.NativeWindow;
/**
* Container and generator of ShellViews.
@ -21,6 +22,7 @@ import org.chromium.base.JNINamespace;
@JNINamespace("content")
public class ShellManager extends FrameLayout {
private NativeWindow mWindow;
private Shell mActiveShell;
private String mStartupUrl = ContentShellActivity.DEFAULT_SHELL_URL;
@ -55,6 +57,13 @@ public class ShellManager extends FrameLayout {
});
}
/**
* @param window The window used to generate all shells.
*/
public void setWindow(NativeWindow window) {
mWindow = window;
}
/**
* Sets the startup URL for new shell windows.
*/
@ -84,6 +93,7 @@ public class ShellManager extends FrameLayout {
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Shell shellView = (Shell) inflater.inflate(R.layout.shell_view, null);
shellView.setSurfaceView(mSurfaceView);
shellView.setWindow(mWindow);
removeAllViews();
if (mActiveShell != null && mActiveShell.getContentView() != null) {

@ -4,6 +4,7 @@ include_rules = [
"+grit/native_theme_resources.h",
"+grit/ui_resources.h",
"+grit/ui_strings.h",
"+jni",
"+net",
"+skia",
"+third_party/mozilla",

@ -0,0 +1,257 @@
// Copyright (c) 2012 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.ui;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.ui.gfx.NativeWindow;
/**
* A dialog that is triggered from a file input field that allows a user to select a file based on
* a set of accepted file types. The path of the selected file is passed to the native dialog.
*/
@JNINamespace("ui")
class SelectFileDialog implements NativeWindow.IntentCallback{
// TODO (aurimas): Swap these constants with AppResources when it gets moved to base to support
// internationalization.
private static final String LOW_MEMORY_ERROR =
"Unable to complete previous operation due to low memory";
private static final String OPENING_FILE_ERROR = "Failed to open selected file";
private static final String IMAGE_TYPE = "image/";
private static final String VIDEO_TYPE = "video/";
private static final String AUDIO_TYPE = "audio/";
private static final String ALL_IMAGE_TYPES = IMAGE_TYPE + "*";
private static final String ALL_VIDEO_TYPES = VIDEO_TYPE + "*";
private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*";
private static final String ANY_TYPES = "*/*";
private static final String CAPTURE_CAMERA = "camera";
private static final String CAPTURE_CAMCORDER = "camcorder";
private static final String CAPTURE_MICROPHONE = "microphone";
private static final String CAPTURE_FILESYSTEM = "filesystem";
private static final String CAPTURE_IMAGE_DIRECTORY = "browser-photos";
private final int mNativeSelectFileDialog;
private List<String> mFileTypes;
private String mCapture; // May be null if no capture parameter was set.
private Uri mCameraOutputUri;
private SelectFileDialog(int nativeSelectFileDialog) {
mNativeSelectFileDialog = nativeSelectFileDialog;
}
/**
* Creates and starts an intent based on the passed fileTypes and capture value.
* @param fileTypes MIME types requested (i.e. "image/*")
* @param capture The capture value as described in http://www.w3.org/TR/html-media-capture/
* @param window The NativeWindow that can show intents
*/
@CalledByNative
private void selectFile(String[] fileTypes, String capture, NativeWindow window) {
mFileTypes = new ArrayList<String>(Arrays.asList(fileTypes));
mCapture = capture;
Intent chooser = new Intent(Intent.ACTION_CHOOSER);
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mCameraOutputUri = Uri.fromFile(getFileForImageCapture());
camera.putExtra(MediaStore.EXTRA_OUTPUT, mCameraOutputUri);
Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Intent soundRecorder = new Intent(
MediaStore.Audio.Media.RECORD_SOUND_ACTION);
// Quick check - if a capture parameter other than filesystem (the default) is specified we
// should just launch the appropriate intent. Otherwise build up a chooser based on the
// accept type and then display that to the user.
if (captureCamera()) {
if (window.showIntent(camera, this, LOW_MEMORY_ERROR)) return;
} else if (captureCamcorder()) {
if (window.showIntent(camcorder, this, LOW_MEMORY_ERROR)) return;
} else if (captureMicrophone()) {
if (window.showIntent(soundRecorder, this, LOW_MEMORY_ERROR)) return;
}
Intent getContentIntent = new Intent(Intent.ACTION_GET_CONTENT);
getContentIntent.addCategory(Intent.CATEGORY_OPENABLE);
ArrayList<Intent> extraIntents = new ArrayList<Intent>();
if (!noSpecificType()) {
// Create a chooser based on the accept type that was specified in the webpage. Note
// that if the web page specified multiple accept types, we will have built a generic
// chooser above.
if (shouldShowImageTypes()) {
extraIntents.add(camera);
getContentIntent.setType("image/*");
} else if (shouldShowVideoTypes()) {
extraIntents.add(camcorder);
getContentIntent.setType("video/*");
} else if (shouldShowAudioTypes()) {
extraIntents.add(soundRecorder);
getContentIntent.setType("audio/*");
}
}
if (extraIntents.isEmpty()) {
// We couldn't resolve an accept type, so fallback to a generic chooser.
getContentIntent.setType("*/*");
extraIntents.add(camera);
extraIntents.add(camcorder);
extraIntents.add(soundRecorder);
}
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS,
extraIntents.toArray(new Intent[] { }));
chooser.putExtra(Intent.EXTRA_INTENT, getContentIntent);
if (!window.showIntent(chooser, this, LOW_MEMORY_ERROR)) onFileNotSelected();
}
/**
* Get a file for the image capture in the CAPTURE_IMAGE_DIRECTORY directory.
*/
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;
}
File photoFile = new File(cameraDataDir.getAbsolutePath() +
File.separator + System.currentTimeMillis() + ".jpg");
return photoFile;
}
/**
* Callback method to handle the intent results and pass on the path to the native
* SelectFileDialog.
* @param window The window that has access to the application activity.
* @param resultCode The result code whether the intent returned successfully.
* @param contentResolver The content resolver used to extract the path of the selected file.
* @param results The results of the requested intent.
*/
@Override
public void onIntentCompleted(NativeWindow window, int resultCode,
ContentResolver contentResolver, Intent results) {
if (resultCode != Activity.RESULT_OK) {
onFileNotSelected();
return;
}
boolean success = false;
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());
success = true;
// 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
// scanner runs).
window.getActivity().sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mCameraOutputUri));
} else {
// We get back a content:// URI from the system if the user picked a file from the
// gallery. The ContentView has functionality that will convert that content:// URI to
// a file path on disk that Chromium understands.
Cursor c = contentResolver.query(results.getData(),
new String[] { MediaStore.MediaColumns.DATA }, null, null, null);
if (c != null) {
if (c.getCount() == 1) {
c.moveToFirst();
String path = c.getString(0);
if (path != null) {
// Not all providers support the MediaStore.DATA column. For example,
// Gallery3D (com.android.gallery3d.provider) does not support it for
// Picasa Web Album images.
nativeOnFileSelected(mNativeSelectFileDialog, path);
success = true;
}
}
c.close();
}
}
if (!success) {
onFileNotSelected();
window.showError(OPENING_FILE_ERROR);
}
}
private void onFileNotSelected() {
nativeOnFileNotSelected(mNativeSelectFileDialog);
}
private boolean noSpecificType() {
// We use a single Intent to decide the type of the file chooser we display to the user,
// which means we can only give it a single type. If there are multiple accept types
// specified, we will fallback to a generic chooser (unless a capture parameter has been
// specified, in which case we'll try to satisfy that first.
return mFileTypes.size() != 1 || mFileTypes.contains(ANY_TYPES);
}
private boolean shouldShowTypes(String allTypes, String specificType) {
if (noSpecificType() || mFileTypes.contains(allTypes)) return true;
return acceptSpecificType(specificType);
}
private boolean shouldShowImageTypes() {
return shouldShowTypes(ALL_IMAGE_TYPES,IMAGE_TYPE);
}
private boolean shouldShowVideoTypes() {
return shouldShowTypes(ALL_VIDEO_TYPES, VIDEO_TYPE);
}
private boolean shouldShowAudioTypes() {
return shouldShowTypes(ALL_AUDIO_TYPES, AUDIO_TYPE);
}
private boolean captureCamera() {
return shouldShowImageTypes() && mCapture != null && mCapture.startsWith(CAPTURE_CAMERA);
}
private boolean captureCamcorder() {
return shouldShowVideoTypes() && mCapture != null &&
mCapture.startsWith(CAPTURE_CAMCORDER);
}
private boolean captureMicrophone() {
return shouldShowAudioTypes() && mCapture != null &&
mCapture.startsWith(CAPTURE_MICROPHONE);
}
private boolean captureFilesystem() {
return mCapture != null && mCapture.startsWith(CAPTURE_FILESYSTEM);
}
private boolean acceptSpecificType(String accept) {
for (String type : mFileTypes) {
if (type.startsWith(accept)) {
return true;
}
}
return false;
}
@CalledByNative
private static SelectFileDialog create(int nativeSelectFileDialog) {
return new SelectFileDialog(nativeSelectFileDialog);
}
private native void nativeOnFileSelected(int nativeSelectFileDialogImpl,
String filePath);
private native void nativeOnFileNotSelected(int nativeSelectFileDialogImpl);
}

@ -0,0 +1,226 @@
// Copyright (c) 2012 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.ui.gfx;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.SparseArray;
import android.widget.Toast;
import org.chromium.base.JNINamespace;
import java.util.HashMap;
/**
* The window that has access to the main activity and is able to create and receive intents,
* and show error messages.
*/
@JNINamespace("ui")
public class NativeWindow {
// Constants used for intent request code bounding.
private static final int REQUEST_CODE_PREFIX = 1000;
private static final int REQUEST_CODE_RANGE_SIZE = 100;
// A string used as a key to store intent errors in a bundle
static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
// Native pointer to the c++ WindowAndroid object.
private int mNativeWindowAndroid = 0;
private int mNextRequestCode = 0;
protected Activity mActivity;
private SparseArray<IntentCallback> mOutstandingIntents;
private HashMap<Integer, String> mIntentErrors;
/**
* An interface that intent callback objects have to implement.
*/
public interface IntentCallback {
/**
* Handles the data returned by the requested intent.
* @param window A window reference.
* @param resultCode Result code of the requested intent.
* @param contentResolver An instance of ContentResolver class for accessing returned data.
* @param data The data returned by the intent.
*/
public void onIntentCompleted(NativeWindow window, int resultCode,
ContentResolver contentResolver, Intent data);
}
/**
* Constructs a Window object, saves a reference to the main activity, and initializes the
* outstanding intent map. NativeWindowAndroid gets lazily loaded on getNativePointer().
* @param activity The main application activity.
*/
public NativeWindow(Activity activity) {
mActivity = activity;
mOutstandingIntents = new SparseArray<IntentCallback>();
mIntentErrors = new HashMap<Integer, String>();
mNativeWindowAndroid = 0;
}
/**
* Destroys the c++ WindowAndroid object if one has been created.
*/
public void destroy() {
if (mNativeWindowAndroid != 0) {
nativeDestroy(mNativeWindowAndroid);
mNativeWindowAndroid = 0;
}
}
/**
* Shows an intent and returns the results to the callback object.
* @param intent The intent that needs to be showed.
* @param callback The object that will receive the results for the intent.
* @return Whether the intent was shown.
*/
public boolean showIntent(Intent intent, IntentCallback callback) {
return showIntent(intent, callback, null);
}
/**
* Shows an intent and returns the results to the callback object.
* @param intent The intent that needs to be showed.
* @param callback The object that will receive the results for the intent.
* @param errorId The id of the error string to be show if activity is paused before intent
* results.
* @return Whether the intent was shown.
*/
public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
String error = null;
try {
error = mActivity.getString(errorId);
} catch (Resources.NotFoundException e) { }
return showIntent(intent, callback, error);
}
/**
* Shows an intent and returns the results to the callback object.
* @param intent The intent that needs to be showed.
* @param callback The object that will receive the results for the intent.
* @param error The error string to be show if activity is paused before intent results.
* @return Whether the intent was shown.
*/
public boolean showIntent(Intent intent, IntentCallback callback, String error) {
int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode;
mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE;
try {
mActivity.startActivityForResult(intent, requestCode);
} catch (ActivityNotFoundException e) {
return false;
}
mOutstandingIntents.put(requestCode, callback);
if (error != null) mIntentErrors.put(requestCode, error);
return true;
}
/**
* Saves the error messages that should be shown if any pending intents would return
* after the application has been put onPause.
* @param bundle The bundle to save the information in onPause
*/
public void saveInstanceState(Bundle bundle) {
bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors);
}
/**
* Restores the error messages that should be shown if any pending intents would return
* after the application has been put onPause.
* @param bundle The bundle to restore the information from onResume
*/
public void restoreInstanceState(Bundle bundle) {
if (bundle == null) return;
Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS);
if (errors instanceof HashMap) {
@SuppressWarnings("unchecked")
HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors;
mIntentErrors = intentErrors;
}
}
/**
* Displays an error message with a provided error message string.
* @param error The error message string to be displayed.
*/
public void showError(String error) {
if (error != null) Toast.makeText(mActivity, error, Toast.LENGTH_SHORT).show();
}
/**
* Displays an error message with a provided error message string id.
* @param errorId The string id of the error message string to be displayed.
*/
public void showError(int errorId) {
String error = null;
try {
error = mActivity.getString(errorId);
} catch (Resources.NotFoundException e) { }
showError(error);
}
/**
* Displays an error message for a nonexistent callback.
* @param error The error message string to be displayed.
*/
protected void showCallbackNonExistentError(String error) {
showError(error);
}
/**
* @return The main application activity.
*/
public Activity getActivity() {
return mActivity;
}
/**
* Responds to the intent result if the intent was created by the native window.
* @param requestCode Request code of the requested intent.
* @param resultCode Result code of the requested intent.
* @param data The data returned by the intent.
* @return Boolean value of whether the intent was started by the native window.
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
IntentCallback callback = mOutstandingIntents.get(requestCode);
mOutstandingIntents.delete(requestCode);
String errorMessage = mIntentErrors.remove(requestCode);
if (callback != null) {
callback.onIntentCompleted(this, resultCode,
mActivity.getContentResolver(), data);
return true;
} else {
if (errorMessage != null) {
showCallbackNonExistentError(errorMessage);
return true;
}
}
return false;
}
/**
* Returns a pointer to the c++ AndroidWindow object and calls the initializer if
* the object has not been previously initialized.
* @return A pointer to the c++ AndroidWindow.
*/
public int getNativePointer() {
if (mNativeWindowAndroid == 0) {
mNativeWindowAndroid = nativeInit();
}
return mNativeWindowAndroid;
}
private native int nativeInit();
private native void nativeDestroy(int nativeWindowAndroid);
}

@ -0,0 +1,24 @@
// Copyright (c) 2012 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.
#include "ui/android/ui_jni_registrar.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "ui/base/dialogs/select_file_dialog_android.h"
#include "ui/gfx/android/window_android.h"
namespace ui {
static base::android::RegistrationMethod kUiRegisteredMethods[] = {
{ "NativeWindow", WindowAndroid::RegisterWindowAndroid },
{ "SelectFileDialog", SelectFileDialogImpl::RegisterSelectFileDialog },
};
bool RegisterJni(JNIEnv* env) {
return RegisterNativeMethods(env, kUiRegisteredMethods,
arraysize(kUiRegisteredMethods));
}
} // namespace ui

@ -0,0 +1,17 @@
// Copyright (c) 2012 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.
#ifndef UI_ANDROID_UI_JNI_REGISTRAR_H_
#define UI_ANDROID_UI_JNI_REGISTRAR_H_
#include <jni.h>
namespace ui {
// Register all JNI bindings necessary for chrome.
bool RegisterJni(JNIEnv* env);
} // namespace ui
#endif // UI_ANDROID_UI_JNI_REGISTRAR_H_

@ -20,6 +20,8 @@
#include "ui/base/dialogs/select_file_dialog_mac.h"
#elif defined(TOOLKIT_GTK)
#include "ui/base/dialogs/gtk/select_file_dialog_impl.h"
#elif defined(OS_ANDROID)
#include "ui/base/dialogs/select_file_dialog_android.h"
#endif
namespace {
@ -84,8 +86,7 @@ SelectFileDialog* SelectFileDialog::Create(Listener* listener,
#elif defined(TOOLKIT_GTK)
return CreateLinuxSelectFileDialog(listener, policy);
#elif defined(OS_ANDROID)
// see crbug.com/116131 to track implemenation of SelectFileDialog
NOTIMPLEMENTED();
return CreateAndroidSelectFileDialog(listener, policy);
#endif
return NULL;

@ -0,0 +1,113 @@
// Copyright (c) 2012 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.
#include "select_file_dialog_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/jni_array.h"
#include "base/android/scoped_java_ref.h"
#include "base/logging.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "jni/SelectFileDialog_jni.h"
#include "ui/gfx/android/window_android.h"
namespace ui {
// static
SelectFileDialogImpl* SelectFileDialogImpl::Create(Listener* listener,
ui::SelectFilePolicy* policy) {
return new SelectFileDialogImpl(listener, policy);
}
void SelectFileDialogImpl::OnFileSelected(JNIEnv* env,
jobject java_object,
jstring filepath) {
if (listener_) {
std::string path = base::android::ConvertJavaStringToUTF8(env, filepath);
listener_->FileSelected(FilePath(path), 0, NULL);
}
is_running_ = false;
}
void SelectFileDialogImpl::OnFileNotSelected(
JNIEnv* env,
jobject java_object) {
if (listener_)
listener_->FileSelectionCanceled(NULL);
is_running_ = false;
}
bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow) const {
return is_running_;
}
void SelectFileDialogImpl::ListenerDestroyed() {
listener_ = NULL;
}
void SelectFileDialogImpl::SelectFileImpl(
ui::SelectFileDialog::Type type,
const string16& title,
const FilePath& default_path,
const SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const std::string& default_extension,
gfx::NativeWindow owning_window,
void* params) {
JNIEnv* env = base::android::AttachCurrentThread();
std::vector<string16> accept_types =
*(reinterpret_cast<std::vector<string16>*>(params));
// The last string in params is expected to be the string with capture value.
ScopedJavaLocalRef<jstring> capture_value =
base::android::ConvertUTF16ToJavaString(env,
StringToLowerASCII(accept_types.back()));
base::android::CheckException(env);
accept_types.pop_back();
// The rest params elements are expected to be accept_types.
ScopedJavaLocalRef<jobjectArray> accept_types_java =
base::android::ToJavaArrayOfStrings(env, accept_types);
Java_SelectFileDialog_selectFile(env, java_object_.obj(),
accept_types_java.obj(),
capture_value.obj(),
owning_window->GetJavaObject().obj());
is_running_ = true;
}
bool SelectFileDialogImpl::RegisterSelectFileDialog(JNIEnv* env) {
return RegisterNativesImpl(env);
}
SelectFileDialogImpl::~SelectFileDialogImpl() {
}
SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener,
ui::SelectFilePolicy* policy)
: ui::SelectFileDialog(listener, policy),
is_running_(false) {
JNIEnv* env = base::android::AttachCurrentThread();
java_object_.Reset(
Java_SelectFileDialog_create(env, reinterpret_cast<jint>(this)));
}
bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() {
NOTIMPLEMENTED();
return false;
}
SelectFileDialog* CreateAndroidSelectFileDialog(
SelectFileDialog::Listener* listener,
SelectFilePolicy* policy) {
return SelectFileDialogImpl::Create(listener, policy);
}
} // namespace ui

@ -0,0 +1,66 @@
// Copyright (c) 2012 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.
#ifndef UI_BASE_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_
#define UI_BASE_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/file_path.h"
#include "ui/base/dialogs/select_file_dialog.h"
namespace ui {
class SelectFileDialogImpl : public ui::SelectFileDialog {
public:
static SelectFileDialogImpl* Create(
Listener* listener,
ui::SelectFilePolicy* policy);
void OnFileSelected(JNIEnv* env, jobject java_object, jstring filepath);
void OnFileNotSelected(JNIEnv* env, jobject java_object);
// From SelectFileDialog
virtual bool IsRunning(gfx::NativeWindow) const OVERRIDE;
virtual void ListenerDestroyed() OVERRIDE;
// Called when it is time to display the file picker.
// params is expected to be a Vector<string16> with accept_types first and
// the capture value as the last element of the vector.
virtual void SelectFileImpl(
ui::SelectFileDialog::Type type,
const string16& title,
const FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const std::string& default_extension,
gfx::NativeWindow owning_window,
void* params) OVERRIDE;
static bool RegisterSelectFileDialog(JNIEnv* env);
protected:
virtual ~SelectFileDialogImpl();
private:
SelectFileDialogImpl(Listener* listener, ui::SelectFilePolicy* policy);
virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE;
base::android::ScopedJavaGlobalRef<jobject> java_object_;
// Stores the state whether select_file_dialog is running.
bool is_running_;
DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl);
};
SelectFileDialog* CreateAndroidSelectFileDialog(
SelectFileDialog::Listener* listener,
SelectFilePolicy* policy);
} // namespace ui
#endif // UI_BASE_DIALOGS_ANDROID_SELECT_FILE_DIALOG_ANDROID_H_

@ -0,0 +1,46 @@
// Copyright (c) 2012 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.
#include "window_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_helper.h"
#include "base/android/scoped_java_ref.h"
#include "jni/NativeWindow_jni.h"
namespace ui {
using base::android::AttachCurrentThread;
using base::android::ScopedJavaLocalRef;
WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj)
: weak_java_window_(env, obj) {
}
void WindowAndroid::Destroy(JNIEnv* env, jobject obj) {
delete this;
}
ScopedJavaLocalRef<jobject> WindowAndroid::GetJavaObject() {
JNIEnv* env = AttachCurrentThread();
return weak_java_window_.get(env);
}
bool WindowAndroid::RegisterWindowAndroid(JNIEnv* env) {
return RegisterNativesImpl(env);
}
WindowAndroid::~WindowAndroid() {
}
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
jint Init(JNIEnv* env, jobject obj) {
WindowAndroid* window = new WindowAndroid(env, obj);
return reinterpret_cast<jint>(window);
}
} // namespace ui

@ -0,0 +1,35 @@
// Copyright (c) 2012 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.
#ifndef UI_GFX_ANDROID_WINDOW_ANDROID_H_
#define UI_GFX_ANDROID_WINDOW_ANDROID_H_
#include <jni.h>
#include "base/android/jni_helper.h"
#include "base/android/scoped_java_ref.h"
namespace ui {
// Android implementation of the activity window.
class WindowAndroid {
public:
WindowAndroid(JNIEnv* env, jobject obj);
void Destroy(JNIEnv* env, jobject obj);
base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
static bool RegisterWindowAndroid(JNIEnv* env);
private:
~WindowAndroid();
JavaObjectWeakGlobalRef weak_java_window_;
DISALLOW_COPY_AND_ASSIGN(WindowAndroid);
};
} // namespace ui
#endif // UI_GFX_ANDROID_WINDOW_ANDROID_H_

@ -110,6 +110,9 @@ struct ANativeWindow;
namespace content {
class ContentViewCore;
}
namespace ui {
class WindowAndroid;
}
#endif
class SkBitmap;
@ -146,7 +149,7 @@ typedef GdkEvent* NativeEvent;
#elif defined(OS_ANDROID)
typedef void* NativeCursor;
typedef content::ContentViewCore* NativeView;
typedef content::ContentViewCore* NativeWindow;
typedef ui::WindowAndroid* NativeWindow;
typedef void* NativeRegion;
typedef jobject NativeEvent;
#endif

@ -98,6 +98,8 @@
],
'all_sources': [
'<@(_common_sources)',
'android/ui_jni_registrar.cc',
'android/ui_jni_registrar.h',
'base/accelerators/accelerator.cc',
'base/accelerators/accelerator.h',
'base/accelerators/accelerator_cocoa.h',
@ -178,6 +180,8 @@
'base/dialogs/gtk/select_file_dialog_impl_kde.cc',
'base/dialogs/select_file_dialog.cc',
'base/dialogs/select_file_dialog.h',
'base/dialogs/select_file_dialog_android.cc',
'base/dialogs/select_file_dialog_android.h',
'base/dialogs/select_file_dialog_factory.cc',
'base/dialogs/select_file_dialog_factory.h',
'base/dialogs/select_file_dialog_mac.h',
@ -398,6 +402,8 @@
'gfx/android/gfx_jni_registrar.h',
'gfx/android/java_bitmap.cc',
'gfx/android/java_bitmap.h',
'gfx/android/window_android.cc',
'gfx/android/window_android.h',
'gfx/blit.cc',
'gfx/blit.h',
'gfx/canvas.cc',
@ -785,6 +791,16 @@
'gfx/platform_font_pango.cc',
'gfx/platform_font_pango.h',
],
'dependencies': [
'ui_java',
'ui_jni_headers',
],
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)/ui',
],
'export_dependent_settings': [
'ui_java',
],
}],
['OS=="android" or OS=="ios"', {
'sources!': [
@ -811,5 +827,36 @@
'ui_unittests.gypi',
]},
],
['OS=="android"' , {
'targets': [
{
'target_name': 'ui_jni_headers',
'type': 'none',
'sources': [
'android/java/src/org/chromium/ui/gfx/NativeWindow.java',
'android/java/src/org/chromium/ui/SelectFileDialog.java',
],
'variables': {
'jni_gen_dir': 'ui',
},
'includes': [ '../build/jni_generator.gypi' ],
},
{
'target_name': 'ui_java',
'type': 'none',
'variables': {
'package_name': 'ui',
'java_in_dir': '../ui/android/java',
},
'dependencies': [
'../base/base.gyp:base_java',
],
'export_dependent_settings': [
'../base/base.gyp:base_java',
],
'includes': [ '../build/java.gypi' ],
},
],
}],
],
}